[英]How to write a good GetHashCode() implementation for a class that is compared by value?
假设我们有这样一个类:
class MyClass
{
public string SomeValue { get; set; }
// ...
}
现在,假设两个MyClass
实例的SomeValue
属性相等。 因此,我覆盖了Object.Equals()
和Object.GetHashCode()
方法来表示它。 Object.GetHashCode()
返回SomeValue.GetHashCode()
但同时我需要遵循以下规则:
但是很明显, SomeValue
可以更改,并且我们之前获得的哈希码可能变为无效。
我只能想到使该类不可变,但是我想知道其他人在这种情况下会做什么。
在这种情况下您会怎么做? 这样的类在设计决策中代表着一个微妙的问题吗?
一般合同规定,如果A.equals(B)为true,则其哈希码必须相同。 如果SomeValue在A中以A.equals(B)不再为真的方式更改,则A.GetHashCode()可以返回与以前不同的值。 可变对象无法缓存GetHashCode(),必须在每次调用该方法时对其进行计算。
本文提供了有关GetHashCode和可变性的详细指南:
http://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/
如果您的GetHashCode()
依赖于某些可变值,则只要值更改,就必须更改哈希值。 否则,您将违反平等法则。
如果您将对象放入HashSet
或作为Dictionary
的键,则需要有人不要求更改哈希的部分。 在这些情况下,您必须确保只要将哈希码存储在这样的容器中就不会更改。 可以通过在编程时简单地解决此问题来手动确保这一点,或者可以向对象提供一些Freeze()
方法。 如果调用此方法,则任何随后尝试设置属性的尝试都会导致某种异常(还应提供一些Defrost()
方法)。 另外,您将对Freeze()
方法的调用放入GetHashCode()
实现中,因此可以确定没有人错误地更改冻结的对象。
最后一个提示:如果您需要在这样的容器中更改对象,只需将其删除,更改(不要忘了解冻)并再次添加即可。
您需要在可变性和为“相等”对象返回相同值的GetHashCode之间进行选择。 通常,当您认为要对可变对象实现“相等”时,您最终会决定拥有“相等阴影”,而实际上并不意味着Object.Equals相等。
对我而言,在任何类型的数据结构中将可变对象作为“键”都是一个很大的危险信号。 例如:
MyObj a = new MyObj("alpha");
MyObj b = new MyObj("beta");
HashSet<MyObj> objs = new HashSet<MyObj>();
objs.Add(a);
objs.Add(b);
// objs.Count == 2
b.SomeValue = "alpha";
// objs.Distinct().Count() == 1, objs.Count == 2
我们严重违反了HashSet<T>
的合同。 这是一个明显的例子,有一些微妙的例子。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.