简体   繁体   English

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

[英]How to write a good GetHashCode() implementation for a class that is compared by value?

Let's say we have such a class: 假设我们有这样一个类:

class MyClass
{
    public string SomeValue { get; set; }

    // ...
}

Now, let's say two MyClass instances are equal when their SomeValue property is equal. 现在,假设两个MyClass实例的SomeValue属性相等。 Thus, I overwrite the Object.Equals() and the Object.GetHashCode() methods to represent that. 因此,我覆盖了Object.Equals()Object.GetHashCode()方法来表示它。 Object.GetHashCode() returns SomeValue.GetHashCode() But at the same time I need to follow these rules: Object.GetHashCode()返回SomeValue.GetHashCode()但同时我需要遵循以下规则:

  1. If two instances of an object are equal, they should return the same hash code. 如果一个对象的两个实例相等,则它们应返回相同的哈希码。
  2. The hash code should not change throughout the runtime. 哈希码在整个运行期间不应更改。

But apparently, SomeValue can change, and the hash code we did get before may turn to be invalid. 但是很明显, SomeValue可以更改,并且我们之前获得的哈希码可能变为无效。

I can only think of making the class immutable, but I'd like to know what others do in this case. 我只能想到使该类不可变,但是我想知道其他人在这种情况下会做什么。

What do you do in such cases? 在这种情况下您会怎么做? Is having such a class represents a subtler problem in the design decisions? 这样的类在设计决策中代表着一个微妙的问题吗?

The general contract says that if A.equals(B) is true, then their hash codes must be the same. 一般合同规定,如果A.equals(B)为true,则其哈希码必须相同。 If SomeValue changes in A in such a way that A.equals(B) is no longer true, then A.GetHashCode() can return a different value than before. 如果SomeValue在A中以A.equals(B)不再为真的方式更改,则A.GetHashCode()可以返回与以前不同的值。 Mutable objects cannot cache GetHashCode(), it must be calculated every time the method is called. 可变对象无法缓存GetHashCode(),必须在每次调用该方法时对其进行计算。

This article has detailed guidelines for GetHashCode and mutability: 本文提供了有关GetHashCode和可变性的详细指南:

http://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/ http://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/

If your GetHashCode() depends on some mutable value you have to change your hash whenever your value changes. 如果您的GetHashCode()依赖于某些可变值,则只要值更改,就必须更改哈希值。 Otherwise you break the equals law. 否则,您将违反平等法则。

The part, that a hash should never be changed, once somebody asked for it, is needed if you put your object into a HashSet or as a key within a Dictionary . 如果您将对象放入HashSet或作为Dictionary的键,则需要有人不要求更改哈希的部分。 In these cases you have to ensure that the hash code won't be changed as long as it is stored in such a container. 在这些情况下,您必须确保只要将哈希码存储在这样的容器中就不会更改。 This can either be ensured manually, by simply taking care of this issue when you program or you could provide some Freeze() method to your object. 可以通过在编程时简单地解决此问题来手动确保这一点,或者可以向对象提供一些Freeze()方法。 If this is called any subsequent try to set a property would lead to some kind of exception (also you should then provide some Defrost() method). 如果调用此方法,则任何随后尝试设置属性的尝试都会导致某种异常(还应提供一些Defrost()方法)。 Additionally you put the call of the Freeze() method into your GetHashCode() implementation and so you can be quite sure that nobody alter a frozen object by mistake. 另外,您将对Freeze()方法的调用放入GetHashCode()实现中,因此可以确定没有人错误地更改冻结的对象。

And just one last tip: If you need to alter a object within such a container, simply remove it, alter it (don't forget to defrost it) and re-add it again. 最后一个提示:如果您需要在这样的容器中更改对象,只需将其删除,更改(不要忘了解冻)并再次添加即可。

You sort of need to choose between mutability and GetHashCode returning the same value for 'equal' objects. 您需要在可变性和为“相等”对象返回相同值的GetHashCode之间进行选择。 Often when you think you want to implement 'equal' for mutable objects, you end up later deciding that you have "shades of equal" and really didn't mean Object.Equals equality. 通常,当您认为要对可变对象实现“相等”时,您最终会决定拥有“相等阴影”,而实际上并不意味着Object.Equals相等。

Having a mutable object as the 'key' in any sort of data structure is a big red flag to me. 对我而言,在任何类型的数据结构中将可变对象作为“键”都是一个很大的危险信号。 For example: 例如:

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

We've badly violated the contract of HashSet<T> . 我们严重违反了HashSet<T>的合同。 This is an obvious example, there are subtle ones. 这是一个明显的例子,有一些微妙的例子。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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