繁体   English   中英

如何为按值比较的类编写良好的GetHashCode()实现?

[英]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()但同时我需要遵循以下规则:

  1. 如果一个对象的两个实例相等,则它们应返回相同的哈希码。
  2. 哈希码在整个运行期间不应更改。

但是很明显, 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM