假设我希望能够比较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
。
思想?
答案 0 :(得分:9)
Equals
的此实施已无效,因为它不是transitive。您应该将Equals
保留为默认实现,并编写一个新方法,如WildcardEquals
(如此处其他答案所示)。
通常,每当您更改Equals
时,如果您希望能够将对象存储在哈希表(例如GetHashCode
)中并使其正常工作,则必须实现Dictionary<TKey, TValue>
正确。如果您确定对象永远不会在哈希表中结束,那么它在理论上是可选的(但在这种情况下更安全也更清晰,可以覆盖它以抛出“NotSupportedException
”或始终返回{ {1}})。
如果覆盖0
,一般合同总是实现GetHashCode
,因为您无法事先确定后来的用户不会将您的对象放在哈希表中。
答案 1 :(得分:6)
在这种情况下,我会创建一个新的或扩展方法WildcardEquals(other)
,而不是使用运算符。
我不建议隐藏这种复杂性。
答案 2 :(得分:4)
从逻辑的角度来看,我们打破了平等的概念。它不再具有传递性。因此,如果是通配符,A==B
和B==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;在通配符字典中,并接收匹配该模式的所有字符串的列表,然后可以在主字典中单独查找。