我读过大约 10 个关于何时以及如何覆盖的不同问题GetHashCode
但还有一些事情我不太明白。大多数实现GetHashCode
基于对象字段的哈希码,但有人引用了GetHashCode
在对象的生命周期中永远不应该改变。如果它所基于的字段是可变的,那么它如何工作?另外,如果我确实希望字典查找等基于引用相等而不是我的覆盖,该怎么办Equals
?
我主要是压倒一切Equals
为了便于单元测试我的序列化代码,我假设序列化和反序列化(在我的例子中为 XML)会杀死引用相等性,所以我想至少确保它的值相等性是正确的。这是重写的坏习惯吗Equals
在这种情况下?基本上在大多数执行代码中我想要引用相等并且我总是使用==
我不会推翻这一点。我应该创建一个新方法吗ValueEquals
或其他东西而不是覆盖Equals
?我曾经假设框架总是使用==
并不是Equals
比较事物,所以我认为重写是安全的Equals
因为在我看来,它的目的是为了如果你想要有一个不同于平等的第二个定义==
操作员。但从阅读其他几个问题来看,情况似乎并非如此。
EDIT:
看来我的意图不清楚,我的意思是 99% 的时间我想要普通的旧引用相等、默认行为,没有惊喜。对于非常罕见的情况,我希望具有值相等,并且我想通过使用显式请求值相等.Equals
代替==
.
当我这样做时,编译器建议我覆盖GetHashCode
也是如此,这就是这个问题的由来。似乎有相互矛盾的目标GetHashCode
当应用于可变对象时,这些对象是:
- If
a.Equals(b)
then a.GetHashCode()
should == b.GetHashCode()
.
- 的价值
a.GetHashCode()
在一生中永远不应该改变a
.
当对象是可变对象时,这些看起来自然是矛盾的,因为如果对象的状态发生变化,我们期望的值.Equals()
改变,这意味着GetHashCode
应该改变以匹配变化.Equals()
, but GetHashCode
不应该改变。
为什么会出现这样的矛盾呢?这些建议是否不适用于可变对象?可能是假设的,但可能值得一提的是,我指的是类而不是结构。
解决:
我将 JaredPar 标记为已接受,但主要用于评论交互。总结一下我从中学到的东西是,实现所有目标并避免边缘情况下可能出现的奇怪行为的唯一方法就是仅覆盖Equals
and GetHashCode
基于不可变字段,或实现IEquatable
。这种似乎削弱了重写的用处Equals
对于引用类型,据我所知,大多数引用类型通常没有不可变字段,除非它们存储在关系数据库中以用主键标识它们。
如果它所基于的字段是可变的,那么它如何工作?
这并不意味着哈希码会随着对象的变化而变化。由于您阅读的文章中列出的所有原因,这是一个问题。不幸的是,这种问题通常只出现在极端情况下。因此,开发人员往往会逃避不良行为。
另外,如果我确实希望字典查找等基于引用相等而不是我覆盖的等于,该怎么办?
只要你实现一个像这样的接口IEquatable<T>
这应该不是问题。大多数字典实现都会选择一个相等比较器,其方式将使用IEquatable<T>
超过 Object.ReferenceEquals。即使没有IEquatable<T>
,大多数将默认调用 Object.Equals() ,然后它将进入您的实现。
基本上,在大多数执行代码中,我想要引用相等,并且我总是使用 == 并且我不会覆盖它。
如果您希望对象的行为具有值相等性,则应覆盖 == 和 != 以强制所有比较的值相等。如果用户确实想要引用相等,他们仍然可以使用 Object.ReferenceEquals。
我曾经假设框架总是使用 == 而不是 Equals 来比较事物
随着时间的推移,BCL 使用的内容发生了一些变化。现在大多数使用相等的情况都需要IEqualityComparer<T>
实例并使用它来实现平等。在未指定的情况下,他们将使用EqualityComparer<T>.Default
找到一个。在最坏的情况下,这将默认调用 Object.Equals
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)