简体   繁体   English

为什么在重载相等运算符时,您希望覆盖GetHashCode和Equals?

[英]Why are you expected to override GetHashCode and Equals when overloading the equality operator?

Failing to override GetHashCode and Equals when overloading the equality operator causes the compiler to produce warnings. 重载Equals运算符时未能覆盖GetHashCodeEquals会导致编译器产生警告。 Why would it be a good idea to change the implementation of either? 为什么改变其中任何一个的实现是个好主意? After reading Eric Lippert's blog post on GetHashCode it's seems like there probably aren't many useful alternatives to GetHashCode's base implementation, why does the compiler I encourage you to change it? 在阅读了Eric Lippert关于GetHashCode的博客文章之后,似乎可能没有很多有用的替代GetHashCode的基础实现,为什么编译器我鼓励你改变它?

Let's suppose you are implementing a class. 我们假设您正在实施一个类。

If you are overloading == then you are producing a type that has value equality as opposed to reference equality. 如果你正在重载==那么你正在生成一个具有值相等而不是引用相等的类型。

Given that, now the question is "how desirable is it to have a class that implements reference equality in .Equals() and value equality in == ?" 鉴于此,现在的问题是“在== .Equals()中实现引用相等的类和在==值相等是多么令人满意?” and the answer is "not very desirable". 答案是“不太理想”。 That seems like a potential source of confusion. 这似乎是混淆的潜在根源。 (And in fact, the company that I now work for, Coverity, produces a defect discovery tool that checks to see if you are confusing value equality with reference equality for precisely this reason. Coincidentally I was just reading the spec for it when I saw your question!) (事实上​​,我现在为公司工作的公司Coverity生产了一个缺陷发现工具,可以检查你是否将价值平等与参考平等混淆,正是出于这个原因。巧合的是,当我看到它时,我只是在读它的规范。你的问题!)

Moreover, if you are going to have a class that implements both value and reference equality, the usual way to do it is to override Equals and leave == alone, not the other way around. 此外,如果您将要有一个实现值和引用相等的类,通常的方法是重写Equals并单独使用== ,而不是相反。

Therefore, given that you have overloaded == , it is strongly suggested that you also override Equals . 因此,假设您有超载== ,强烈建议您也重写Equals

If you are overriding Equals to produce value equality then you are required to override GetHashCode to match, as you know if you've read my article that you linked to. 如果要重写Equals以产生值相等,则需要覆盖GetHashCode以匹配,因为您知道是否已阅读我链接到的文章。

If you don't override Equals() when you override == you will have some amazingly bad code. 如果你在覆盖==时不重写Equals() ,你将会遇到一些非常糟糕的代码。

How would you feel about this happening? 你觉得这件事怎么样?

if (x == y)
{
   if (!x.Equals(y))
       throw new InvalidOperationException("Wut?");
}

Here's an example. 这是一个例子。 Given this class: 鉴于此类:

class Test
{
    public int Value;
    public string Name;

    public static bool operator==(Test lhs, Test rhs)
    {
        if (ReferenceEquals(lhs, rhs))
            return true;

        if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null))
            return false;

        return lhs.Value == rhs.Value;
    }

    public static bool operator!=(Test lhs, Test rhs)
    {
        return !(lhs == rhs);
    }
}

This code will behave oddly: 这段代码表现得很奇怪:

Test test1 = new Test { Value = 1, Name = "1" };
Test test2 = new Test { Value = 1, Name = "2" };

if (test1 == test2)
    Console.WriteLine("test1 == test2"); // This gets printed.
else
    Console.WriteLine("test1 != test2");

if (test1.Equals(test2))
    Console.WriteLine("test1.Equals(test2)");
else
    Console.WriteLine("NOT test1.Equals(test2)"); // This gets printed!

You do NOT want this! 想要这个!

My guess is that the compiler takes its clues from your actions, and decides that since you find it important to provide an alternative implementation of the equality operator, then you probably want the object equality to remain consistent with your new implementation of == . 我的猜测是编译器从你的动作中获取线索,并决定由于你发现提供相等运算符的替代实现很重要,那么你可能希望对象相等性与你的新实现==保持一致。 After all, you do not want the two equality comparisons to mean drastically different things, otherwise your program would be hard to understand even on a very basic level. 毕竟,你不希望两个相等的比较意味着完全不同的东西,否则你的程序即使在非常基础的层面上也很难理解。 Therefore, the compiler thinks that you should redefine Equals as well. 因此,编译器认为您也应该重新定义Equals

Once you provide an alternative implementation Equals , however, you need to modify GetHashCode to stay consistent with the equality implementation. 但是,一旦提供了替代实现Equals ,就需要修改GetHashCode以保持与相等实现的一致性。 Hence the compiler warns you that your implementation might be incomplete, and suggests overriding both Equals and GetHashCode . 因此编译器会警告您实现可能不完整,并建议覆盖EqualsGetHashCode

If you don't overload the Equals method too, then using it might give different results from the ones you'd get with the operator. 如果你不重载Equals方法,那么使用它可能会给你与运算符得到的结果不同。 Like, if you overload = for integers... 就像,如果你重载=整数...

int i = 1;
(1 == 1) == (i.Equals(1))

Could evaluate to false. 可以评估为假。

For the same reason, you should reimplement the GetHashCode method so you don't mess up with hashtables and such other structures that rely on hash comparisons. 出于同样的原因,您应该重新实现GetHashCode方法,这样您就不会搞乱哈希表以及依赖哈希比较的其他结构。

Notice I'm saying "might" and "could", not "will". 请注意,我说“可能”和“可能”,而不是“将”。 The warnings are there just as a reminder that unexpected things might happen if you don't follow its suggestions. 这些警告只是提醒您,如果您不遵循其建议,可能会发生意外情况。 Otherwise you'd get errors instead of warnings. 否则你会得到错误而不是警告。

The documentation is pretty clear about this: 文档非常清楚:

The GetHashCode method can be overridden by a derived type. GetHashCode方法可以被派生类型覆盖。 Value types must override this method to provide a hash function that is appropriate for that type and to provide a useful distribution in a hash table. 值类型必须覆盖此方法,以提供适合该类型的哈希函数,并在哈希表中提供有用的分布。 For uniqueness, the hash code must be based on the value of an instance field or property instead of a static field or property. 为了唯一性,哈希码必须基于实例字段或属性的值,而不是静态字段或属性。

Objects used as a key in a Hashtable object must also override the GetHashCode method because those objects must generate their own hash code. 用作Hashtable对象中的键的对象还必须覆盖GetHashCode方法,因为这些对象必须生成自己的哈希代码。 If an object used as a key does not provide a useful implementation of GetHashCode, you can specify a hash code provider when the Hashtable object is constructed. 如果用作键的对象不提供GetHashCode的有用实现,则可以在构造Hashtable对象时指定哈希代码提供程序。 Prior to the .NET Framework version 2.0, the hash code provider was based on the System.Collections.IHashCodeProvider interface. 在.NET Framework 2.0版之前,哈希代码提供程序基于System.Collections.IHashCodeProvider接口。 Starting with version 2.0, the hash code provider is based on the System.Collections.IEqualityComparer interface. 从2.0版开始,哈希代码提供程序基于System.Collections.IEqualityComparer接口。

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

相关问题 重写Equals和GetHashCode不一定会覆盖相等重载运算符 - Overriding Equals and GetHashCode doesn't necessarily override equality overloading operator 使用 GetHashCode 在 Equals 覆盖中测试相等性 - Using GetHashCode to test equality in Equals override 为什么在重写 Equals 方法时重写 GetHashCode 很重要? - Why is it important to override GetHashCode when Equals method is overridden? 如何用两种不同的相等组合覆盖Equals和GetHashCode - How to override Equals and GetHashCode with two different combinations of equality C#运算符重载==和pragma警告660和661在不相关时不覆盖Equals和GetHashCode - C# operator overloading == and pragma warnings 660 & 661 not overriding Equals and GetHashCode when irrelevant 在结构中,通过Equals实现operator ==是否有效,但不能覆盖Equals和GetHashCode? - In a struct, is it valid to implement operator== via Equals, but not override Equals and GetHashCode? 为什么KeyValuePair不会覆盖Equals()和GetHashCode()? - Why does KeyValuePair not override Equals() and GetHashCode()? Equals,GetHashCode,EqualityComparers和模糊平等 - Equals, GetHashCode, EqualityComparers and fuzzy equality 在==运算符覆盖中使用GetHashCode - Using GetHashCode in == operator override C#运算符重载:Object.Equals(object o)和Object.GetHashCode() - C# operator overloading: Object.Equals(object o) & Object.GetHashCode()
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM