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

时间:2013-05-20 18:04:22

标签: c# .net operator-overloading

重载等于运算符时未能覆盖GetHashCodeEquals会导致编译器产生警告。为什么改变其中任何一个的实现是个好主意?在阅读Eric Lippert's blog post on GetHashCode后,似乎GetHashCode的基本实现可能没有太多有用的替代方案,为什么编译器我鼓励你改变它?

5 个答案:

答案 0 :(得分:9)

假设您正在实施一个类。

如果您正在重载==,那么您将生成一个值相等而非引用相等的类型。

鉴于此,现在的问题是“在.Equals()中实现引用相等的类和==中的值相等是多么令人满意?”答案是“不太理想”。这似乎是混淆的潜在根源。 (事实上​​,我现在为公司工作的公司Coverity生产了一个缺陷发现工具,可以检查你是否将价值平等与参考平等混淆,正是出于这个原因。巧合的是,当我看到它时,我只是在读它的规范。你的问题!)

此外,如果您要创建一个同时实现值和引用相等的类,通常的方法是覆盖Equals并单独留下==,而不是相反。

因此,鉴于您已超载==,强烈建议您也覆盖Equals

如果您要覆盖Equals以产生价值相等,则必需覆盖GetHashCode以匹配,因为您知道如果您已阅读我关联的文章到。

答案 1 :(得分:3)

如果覆盖Equals()时未覆盖==,则会出现一些非常糟糕的代码。

你对这种情况有什么看法?

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

这是一个例子。鉴于此课程:

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);
    }
}

这段代码表现得很奇怪:

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!

NOT 想要这个!

答案 2 :(得分:1)

我的猜测是编译器从你的动作中获取线索,并决定由于你发现提供等于运算符的替代实现很重要,那么你可能希望对象相等与你的新实现保持一致{ {1}}。毕竟,你不希望两个相等的比较意味着完全不同的东西,否则你的程序即使在非常基础的层面上也很难理解。因此,编译器认为您也应该重新定义==

但是,一旦提供了替代实现Equals,就需要修改Equals以保持与相等实现的一致性。因此,编译器会警告您,您的实现可能不完整,并建议覆盖GetHashCodeEquals

答案 3 :(得分:0)

如果您没有重载Equals方法,那么使用它可能会给您与运营商获得的结果不同。比如,如果你为整数重载= ......

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

可以评估为假。

出于同样的原因,你应该重新实现GetHashCode方法,这样你就不会搞乱哈希表以及依赖哈希比较的其他结构。

注意我说“可能”和“可能”,而不是“会”。这些警告只是提醒您,如果您不遵循其建议,可能会发生意外情况。否则你会收到错误而不是警告。

答案 4 :(得分:0)

documentation对此很清楚:

  

GetHashCode方法可以被派生类型覆盖。值   类型必须覆盖此方法以提供哈希函数   适合该类型并在a中提供有用的分布   哈希表。为了唯一性,哈希码必须基于值   实例字段或属性而不是静态字段或   属性。

     

在Hashtable对象中用作键的对象也必须覆盖   GetHashCode方法因为那些对象必须生成自己的哈希   码。如果用作键的对象不提供有用的   实现GetHashCode,您可以指定哈希码提供程序   何时构造Hashtable对象。在.NET Framework之前   版本2.0,哈希代码提供程序基于   System.Collections.IHashCodeProvider接口。从版本开始   2.0,哈希码提供程序基于System.Collections.IEqualityComparer接口。