在具有通配符Equatability的类上实现GetHashCode

时间:2014-07-22 15:37:28

标签: c# wildcard hashcode iequatable

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

e.g。 如果-1是外卡,那么

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

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

中具有相关逻辑

但如果你压倒GetHashCode,我一直认为你或多或少必须实施.Equals()。虽然它没有被编译器强制执行,但我一直认为如果你不这样做那么你做错了。

除非我没有看到如何在不违反合同的情况下实现.GetHashCode()(Equal的对象具有不同的哈希值),或者只是将实现设为return 1

思想?

9 个答案:

答案 0 :(得分:9)

Equals的此实施已无效,因为它不是transitive。您应该将Equals保留为默认实现,并编写一个新方法,如WildcardEquals(如此处其他答案所示)。

通常,每当您更改Equals时,如果您希望能够将对象存储在哈希表(例如GetHashCode)中并使其正常工作,则必须实现Dictionary<TKey, TValue>正确。如果您确定对象永远不会在哈希表中结束,那么它在理论上是可选的(但在这种情况下更安全也更清晰,可以覆盖它以抛出“NotSupportedException”或始终返回{ {1}})。

如果覆盖0,一般合同总是实现GetHashCode,因为您无法事先确定后来的用户不会将您的对象放在哈希表中。

答案 1 :(得分:6)

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

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

答案 2 :(得分:4)

从逻辑的角度来看,我们打破了平等的概念。它不再具有传递性。因此,如果是通配符,A==BB==C并不代表A==C

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

答案 3 :(得分:3)

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

public override int GetHashCode()
{
    return this.Length.GetHashCode()
}

答案 4 :(得分:2)

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

答案 5 :(得分:2)

GetHashCode通常只有在您要将类的元素存储在某种集合中时才是重要的,例如集合。如果是这种情况,那么我认为你不会能够实现一致的语义,因为@AlexD指出相等不再是传递性的。

例如,(使用字符串globs而不是整数列表)如果将字符串“A”,“B”和“*”添加到集合中,则您的集合将以一个或两个元素结束,具体取决于命令你添加它们。

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

答案 6 :(得分:2)

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

如果你把它放在一个hashmap或类似的东西中它可能是低效的,但这可能没问题(非相等的hashcode意味着不相等,但是相同的hashcode意味着什么都没有。)

我认为这是唯一有效的选择。 Hashcodes本质上是作为快速查找内容的键,并且由于您的通配符必须匹配每个项目,因此其查找键必须等于每个项目的键,因此它们必须全部相同。

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

答案 7 :(得分:1)

因为你已经改变了&#34;等于&#34;意味着(添加通配符会显着改变事物)然后你已经超出正常使用Equals和GetHashCode的范围。它只是一个建议,在你的情况下似乎它不适合。所以不用担心。

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

答案 8 :(得分:1)

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

许多集合将有点使用以不实现等价关系的方式实现Equals的对象,只要任何两个可能相互比较的对象始终返回相同的哈希值码。这样做通常需要许多比较不等的东西来返回相同的哈希码,尽管取决于支持哪种类型的通配符,可以在一定程度上分离项目。

例如,如果特定字符串支持的唯一通配符表示&#34;一个或多个数字&#34;的任意字符串,则可以通过转换连续数字的所有序列和/或字符串来对字符串进行散列将通配符数字转换为单个&#34;数字串&#34;通配符。如果#代表任何数字,那么字符串abc123,abc#,abc456和abc#93#22#7都将被散列为与abc#相同的值,但是abc#b,abc123b等可以散列到不同的数字值。根据字符串的分布,这些区别可能会或可能不会产生比返回常量值更好的性能。

请注意,即使以这样的方式实现GetHashCode,使得相等的对象产生相等的哈希值,如果相等方法没有实现等价关系,某些集合仍可能表现得很奇怪。例如,如果集合foo包含带有键的项目&#34; abc1&#34;和&#34; abc2&#34;,尝试访问foo["abc#"]可能会随意返回第一项或第二项。试图删除密钥&#34; abc#&#34;可以随意删除一个或两个项目,或者在删除一个项目后可能会失败(它的预期后置条件不会被满足,因为abc#即使在删除后也会在集合中。)

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