给定以下类别

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很重要?


当前回答

从C#9(.net5或.netcore3.1)开始,您可能希望使用记录,因为默认情况下它使用基于值的相等。

其他回答

我们有两个问题要解决。

如果对象可以更改。此外,对象通常不会用于依赖于GetHashCode()的集合。因此实现GetHashCode()通常不值得,或者不值得可能的如果有人将您的对象放入调用GetHashCode(),并且您已重写Equals()GetHashCode()的行为方式正确,此人可能会花费数天追踪问题。

因此,默认情况下我会这样做。

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()
    {
        // Some comment to explain if there is a real problem with providing GetHashCode() 
        // or if I just don't see a need for it for the given class
        throw new Exception("Sorry I don't know what GetHashCode should do for this class");
    }
}

怎么样:

public override int GetHashCode()
{
    return string.Format("{0}_{1}_{2}", prop1, prop2, prop3).GetHashCode();
}

假设性能不是问题:)

实际上,要正确实现GetHashCode()非常困难,因为除了Marc已经提到的规则之外,哈希代码在对象的生命周期内不应该改变。因此,用于计算哈希代码的字段必须是不可变的。

当我与NHibernate合作时,我终于找到了解决这个问题的方法。我的方法是根据对象的ID计算哈希代码。ID只能通过构造函数设置,因此如果您想更改ID(这是非常不可能的),您必须创建一个具有新ID的新对象,从而创建一个新的哈希代码。这种方法最适合GUID,因为您可以提供一个随机生成ID的无参数构造函数。

是的,如果您的项将用作字典或HashSet<T>等中的键,这一点很重要,因为这是用来将项分组到桶中的(在没有自定义IEqualityComparer<T>的情况下)。如果两个项的哈希代码不匹配,则它们可能永远不会被视为相等(Equals将永远不会被调用)。

GetHashCode()方法应反映Equals逻辑;规则如下:

如果两个值相等(Equals(…)==true),则它们必须为GetHashCode()返回相同的值如果GetHashCode()相等,则它们不必相同;这是一个冲突,将调用Equals来查看它是否是真正的相等。

在本例中,看起来“return FooId;”是一个合适的GetHashCode()实现。如果您正在测试多个财产,通常使用如下代码组合它们,以减少对角线冲突(即,使新的Foo(3,5)具有与新Foo(5,3)不同的哈希代码):

在现代框架中,HashCode类型具有帮助您从多个值创建哈希代码的方法;在较旧的框架上,您需要不使用,因此类似于:

unchecked // only needed if you're compiling with arithmetic checks enabled
{ // (the default compiler behaviour is *disabled*, so most folks won't need this)
    int hash = 13;
    hash = (hash * 7) + field1.GetHashCode();
    hash = (hash * 7) + field2.GetHashCode();
    ...
    return hash;
}

哦-为了方便,您也可以考虑提供==和!=运算符。


当你犯了这个错误时会发生什么。

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

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