给定以下类别

public class Foo
{
    public int FooId { get; set; }
    public string FooName { get; set; }

    public override bool Equals(object obj)
    {
        Foo fooItem = obj as Foo;

        if (fooItem == null) 
        {
           return false;
        }

        return fooItem.FooId == this.FooId;
    }

    public override int GetHashCode()
    {
        // Which is preferred?

        return base.GetHashCode();

        //return this.FooId.GetHashCode();
    }
}

我重写了Equals方法,因为Foo表示Foos表的一行。哪个是重写GetHashCode的首选方法?

为什么重写GetHashCode很重要?


当前回答

从.NET 4.7开始,重写GetHashCode()的首选方法如下所示。如果目标是较旧的.NET版本,请包含System.ValueTuple nuget包。

// C# 7.0+
public override int GetHashCode() => (FooId, FooName).GetHashCode();

在性能方面,该方法将优于大多数复合哈希代码实现。ValueTuple是一个结构,因此不会有任何垃圾,而底层算法的速度也很快。

其他回答

只需补充以上答案:

如果不重写Equals,则默认行为是比较对象的引用。这同样适用于哈希码——默认实现通常基于引用的内存地址。因为您确实重写了Equals,这意味着正确的行为是比较您在Equals上实现的任何内容,而不是比较引用,所以您应该对哈希代码执行同样的操作。

您的类的客户端将期望哈希代码具有与equals方法类似的逻辑,例如,使用IEqualityComparer的linq方法首先比较哈希代码,只有当它们相等时,他们才会比较equals()方法,如果我们不实现哈希代码,则该方法运行起来可能会更昂贵,equal对象可能会有不同的散列码(因为它们有不同的内存地址),并且会被错误地确定为不相等(Equals()甚至不会命中)。

此外,除了在字典中使用对象时可能找不到对象的问题(因为它是由一个哈希码插入的,当你查找它时,默认的哈希码可能会不同,而且Equals()甚至不会被调用,正如Marc Gravell在回答中解释的那样,您还引入了字典或哈希集概念的冲突,该概念不应允许相同的键-您已经声明,当重写Equals时,这些对象本质上是相同的,因此您不希望它们都作为数据结构上的不同键,而假设它们具有唯一键。但是因为它们有不同的哈希码,所以“相同”的密钥将被插入为不同的密钥。

哈希代码用于基于哈希的集合,如Dictionary、Hashtable、HashSet等。该代码的目的是通过将特定对象放入特定组(bucket)来快速地对其进行预排序。当您需要从哈希集合中检索该对象时,这种预排序非常有助于找到该对象,因为代码必须在一个桶中而不是在它包含的所有对象中搜索该对象。哈希码的分布越好(唯一性越好),检索速度越快。在理想情况下,每个对象都有一个唯一的哈希码,找到它是一个O(1)操作。在大多数情况下,它接近O(1)。

从.NET 4.7开始,重写GetHashCode()的首选方法如下所示。如果目标是较旧的.NET版本,请包含System.ValueTuple nuget包。

// C# 7.0+
public override int GetHashCode() => (FooId, FooName).GetHashCode();

在性能方面,该方法将优于大多数复合哈希代码实现。ValueTuple是一个结构,因此不会有任何垃圾,而底层算法的速度也很快。

这是因为框架要求两个相同的对象必须具有相同的哈希代码。如果重写equals方法来对两个对象进行特殊比较,并且该方法认为这两个对象是相同的,那么两个对象的哈希代码也必须相同。(字典和哈希表依赖于这一原则)。

我的理解是,原始的GetHashCode()返回对象的内存地址,因此如果您希望比较两个不同的对象,则必须重写它。

编辑:这是错误的,原始的GetHashCode()方法无法确保2个值相等。虽然相等的对象返回相同的哈希代码。