8

假设我希望能够比较 2 个整数列表并将一个特定值视为通配符。

例如,如果 -1 是通配符,则

{1,2,3,4} == {1,2,-1,4} //returns true

我正在编写一个类来包装所有这些逻辑,因此它实现IEquatable并具有相关逻辑public override bool Equals()

但是我一直认为,GetHashCode如果您覆盖.Equals(). 当然它不是由编译器强制执行的,但我的印象是,如果你不这样做,那么你做错了。

除了我看不到如何在.GetHashCode()不破坏其合同(Equal 的对象具有不同的哈希值)或仅让实现为return 1.

想法?

4

9 回答 9

9

这个实现Equals已经是无效的,因为它不是传递的。您可能应该离开Equals默认实现,并编写一个新方法WildcardEquals(如此处其他答案中所建议的那样)。

通常,无论何时更改EqualsGetHashCode如果您希望能够将对象存储在哈希表(例如 a Dictionary<TKey, TValue>)中并使其正常工作,就必须实现。如果您确定对象永远不会出现在哈希表中,那么它在理论上是可选的(但在这种情况下,覆盖它以抛出 " NotSupportedException" 或总是 return会更安全、更清晰0)。

GetHashCode如果您 override ,一般合同是始终实施Equals,因为您不能总是提前确定以后的用户不会将您的对象放入哈希表中。

于 2014-07-22T15:49:18.900 回答
6

在这种情况下,我将创建一个新的或扩展方法,WildcardEquals(other)而不是使用运算符。

我不建议隐藏这种复杂性。

于 2014-07-22T15:43:10.943 回答
4

从逻辑上看,我们打破了平等的概念。它不再是可传递的。所以在通配符的情况下,A==B并不B==C意味着A==C.

从技术角度来看,返回相同的值GetHashCode()并不是不可原谅的。

于 2014-07-22T15:51:53.543 回答
3

我看到的唯一可能的想法是至少利用长度,例如:

public override int GetHashCode()
{
    return this.Length.GetHashCode()
}
于 2014-07-22T15:41:21.313 回答
2

这是推荐的,但根本不是强制性的。如果您不需要 的自定义实现GetHashCode,请不要这样做。

于 2014-07-22T15:41:20.740 回答
2

GetHashCode通常仅当您要将类的元素存储在某种集合(例如集合)中时才重要。如果是这种情况,那么我认为您将无法实现一致的语义,因为@AlexD 指出相等不再是可传递的。

例如,(使用字符串 glob 而不是整数列表)如果您将字符串“A”、“B”和“*”添加到集合中,您的集合最终将包含一个或两个元素,具体取决于您添加的顺序他们进去。

如果这不是您想要的,那么我建议将通配符匹配放入一个新方法(例如EquivalentTo())中,而不是重载相等。

于 2014-07-22T15:48:16.690 回答
2

让 GetHashCode() 始终返回一个常量是满足 equals/hashcode 约束的唯一“合法”方式。

如果你把它放在一个 hashmap 或类似的东西中,它可能会效率低下,但这可能很好(不相等的哈希码意味着不相等,但相等的哈希码意味着什么)。

我认为这是那里唯一可能的有效选择。哈希码本质上是作为快速查找的键存在的,并且由于您的通配符必须匹配每个项目,因此它的查找键必须等于每个项目的键,因此它们都必须相同。

正如其他人所指出的那样,这不是 equals 通常的用途,并且打破了许多其他事物可能用于 equals 的假设(例如传递性 - 编辑:事实证明这实际上是合同要求,所以不行),所以它是绝对值得至少考虑手动比较这些,或者使用明确独立的相等比较器。

于 2014-07-22T15:49:22.020 回答
1

由于您已经更改了“等于”的含义(添加通配符会极大地改变事情),那么您已经超出了 Equals 和 GetHashCode 的正常使用范围。这只是一个建议,在您的情况下,它似乎不适合。所以不用担心。

也就是说,请确保您没有在可能使用 GetHashCode 的地方使用您的课程。如果您不注意的话,这会给您带来很多麻烦并且很难调试。

于 2014-07-22T15:43:45.930 回答
1

通常期望Equals(Object)并且IEquatable<T>.Equals(T)应该实现等价关系,这样如果观察到 X 等于 Y,并且观察到 Y 等于 Z,并且没有任何项目被修改,则可以假设 X 等于Z; 此外,如果 X 等于 Y 并且 Y 不等于Z,则可以假定 X 也不等于 Z。通配符和模糊比较方法不实现等价关系,因此Equals一般不应该用这种语义来实现。

许多集合将有点与以Equals不实现等价关系的方式实现的对象一起工作,前提是任何两个可能相互比较相等的对象总是返回相同的哈希码。这样做通常需要许多比较不相等的东西来返回相同的哈希码,尽管根据支持的通配符类型,可能在某种程度上分离项目。

例如,如果特定字符串支持的唯一通配符表示“一个或多个数字的任意字符串”,则可以通过将所有连续数字序列和/或数字字符串通配符转换为单个“字符串位数”通配符。如果 # 代表任何数字,则字符串 abc123、abc#、abc456 和 abc#93#22#7 将全部散列到与 abc# 相同的值,但 abc#b、abc123b 等可以散列到不同的值价值。根据字符串的分布,这种区别可能会也可能不会产生比返回常量值更好的性能。

请注意,即使GetHashCode以相等对象产生相等哈希的方式实现,如果相等方法未实现等价关系,某些集合仍可能表现异常。例如,如果集合foo包含键为“abc1”和“abc2”的项目,则尝试访问foo["abc#"]可能会任意返回第一项或第二项。尝试删除键“abc#”可能会任意删除一项或两项,或者在删除一项后可能会失败(其预期的后置条件不会满足,因为abc#即使在删除后也会在集合中)。

与其尝试Equals比较哈希码相等性,另一种方法是使用一个字典,该字典为每个可能匹配至少一个主集合字符串的通配符字符串保存一个它可能匹配的字符串列表。因此,如果有许多匹配 abc# 的字符串,它们可能都有不同的哈希码;如果用户输入“abc#”作为搜索请求,系统将在通配符字典中查找“abc#”并接收与该模式匹配的所有字符串的列表,然后可以在主字典中单独查找.

于 2014-07-22T20:41:22.380 回答