Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/273.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 重载相等运算符时,为什么要重写GetHashCode和Equals?_C#_.net_Operator Overloading - Fatal编程技术网

C# 重载相等运算符时,为什么要重写GetHashCode和Equals?

C# 重载相等运算符时,为什么要重写GetHashCode和Equals?,c#,.net,operator-overloading,C#,.net,Operator Overloading,重载相等运算符时未能重写GetHashCode和Equals,会导致编译器产生警告。为什么改变这两种方法的实施都是一个好主意?阅读后,似乎没有太多有用的替代GetHashCode的基本实现的方法,为什么编译器我鼓励您更改它?如果您没有重载Equals方法,那么使用它可能会得到与使用运算符得到的结果不同的结果。比如,如果您为整数重载= int i = 1; (1 == 1) == (i.Equals(1)) 可能评估为错误 出于同样的原因,您应该重新实现GetHashCode方法,这样就不会弄乱

重载相等运算符时未能重写
GetHashCode
Equals
,会导致编译器产生警告。为什么改变这两种方法的实施都是一个好主意?阅读后,似乎没有太多有用的替代GetHashCode的基本实现的方法,为什么编译器我鼓励您更改它?

如果您没有重载
Equals
方法,那么使用它可能会得到与使用运算符得到的结果不同的结果。比如,如果您为整数重载
=

int i = 1;
(1 == 1) == (i.Equals(1))
可能评估为错误

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

注意,我说的是“可能”和“可能”,而不是“意志”。这些警告只是提醒你,如果你不听从它的建议,可能会发生意想不到的事情。否则,您将得到错误而不是警告。

非常清楚这一点:

GetHashCode方法可以由派生类型重写。价值 类型必须重写此方法以提供 适用于该类型,并在 哈希表。为了唯一性,哈希代码必须基于值 用于实例字段或属性而不是静态字段或属性 财产

在哈希表对象中用作键的对象也必须重写 GetHashCode方法,因为这些对象必须生成自己的哈希 代码。如果用作键的对象未提供有用的 实现GetHashCode时,可以指定哈希代码提供程序 当构建哈希表对象时。在.NET框架之前 版本2.0时,哈希代码提供程序基于 System.Collections.IHashCodeProvider接口。从版本开始 哈希代码提供程序基于System.Collections.IEqualityComparer接口


我的猜测是,编译器从您的操作中获得了线索,并决定,既然您发现提供等式运算符的替代实现很重要,那么您可能希望对象等式与
=
的新实现保持一致。毕竟,您不希望这两个相等比较意味着截然不同的事情,否则您的程序即使在非常基本的层面上也很难理解。因此,编译器认为您也应该重新定义
等于


但是,一旦提供了替代实现
Equals
,就需要修改
GetHashCode
,以与equality实现保持一致。因此,编译器警告您,您的实现可能不完整,并建议重写
Equals
GetHashCode
如果您在重写
=
时不重写
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!

你不想要这个

让我们假设您正在实现一个类

如果重载
==
,则生成的类型具有值相等而不是引用相等

考虑到这一点,现在的问题是“有一个类在
.Equals()
中实现引用相等,在
==
中实现值相等有多理想?”答案是“不太理想”。这似乎是一个潜在的混乱来源。(事实上,我现在为Coverity工作的公司生产了一个缺陷发现工具,可以检查您是否正因为这个原因而混淆了值相等和引用相等。巧合的是,当我看到您的问题时,我正在阅读规范!)

此外,如果您将拥有一个同时实现值和引用相等的类,那么通常的方法是重写
Equals
,并将
==
单独保留,而不是相反

因此,考虑到您已经重载了
=
,强烈建议您也重写
Equals


如果您要重写
Equals
以产生值相等,那么您需要重写
GetHashCode
以进行匹配,如果您已经阅读了链接到的我的文章,您就会知道。

编译器不鼓励您。框架设计者也没有。他们只是给你选择权。请随意使用默认实现。@DavidHeffernan生成警告的编译器在我看来无疑是“鼓励”。@Servy这是一种不正确的阅读方式。编译器的意思是,如果必须这样做,请正确执行。@DavidHeffernan如果您重写
=
运算符,编译器将鼓励您重写
等于和
GetHashCode
。这就是OP所说的。显然,它并不总是鼓励你在任何情况下这样做;OP没有断言它有。OP声称它鼓励你在做X时这样做,并问为什么。说“它不鼓励你”是错误的。大卫·费弗南:我认为你在第一次答复中说的话有点误导人。有些人可能会认为它的意思是“你可以忽略编译器警告”,我很确定你不是这个意思。如果你收到那个警告,你不应该忽略它。事实上…那个代码是有效的…尽管很奇怪。不过,它确实是一个非常有效的例子,说明了引用和值相等之间的区别。X和Y都可以是值为1的整数,但它们不是同一个对象……虽然对于整数,我认为.Equals应该是真的,但这不是重点。这取决于你想检查什么我想…如果你想检查它们是否是eq