给定以下类别

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


当前回答

重写Equal()时,请不要忘记检查obj参数是否为null。并比较类型。

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

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

    return fooItem.FooId == this.FooId;
}

原因是:与null比较时,Equals必须返回false。另请参见http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx

其他回答

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

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

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

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

在我看来,考虑到公共财产,下面使用反射似乎是一个更好的选择,因为在此情况下,您不必担心财产的添加/删除(尽管不太常见)。我发现这也表现得更好。(使用诊断学秒表比较时间)。

    public int getHashCode()
    {
        PropertyInfo[] theProperties = this.GetType().GetProperties();
        int hash = 31;
        foreach (PropertyInfo info in theProperties)
        {
            if (info != null)
            {
                var value = info.GetValue(this,null);
                if(value != null)
                unchecked
                {
                    hash = 29 * hash ^ value.GetHashCode();
                }
            }
        }
        return hash;  
    }

重写Equal()时,请不要忘记检查obj参数是否为null。并比较类型。

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

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

    return fooItem.FooId == this.FooId;
}

原因是:与null比较时,Equals必须返回false。另请参见http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx

您应该始终保证,如果两个对象相等(由Equals()定义),它们应该返回相同的哈希代码。正如其他一些评论所说,理论上,如果对象永远不会在HashSet或Dictionary等基于哈希的容器中使用,则这不是强制性的。但我建议你始终遵守这条规则。原因很简单,因为人们很容易将集合从一种类型更改为另一种类型,以便实际提高性能或以更好的方式传递代码语义。

例如,假设我们在列表中保留一些对象。一段时间后,有人意识到HashSet是一个更好的选择,因为它具有更好的搜索特性。这是我们可能陷入麻烦的时候。List将在内部为类型使用默认的相等比较器,在您的情况下,这意味着Equals,而HashSet使用GetHashCode()。如果两者的行为不同,那么您的程序也会不同。请记住,这些问题不是最容易解决的。

我在一篇博客文章中总结了这种行为和其他GetHashCode()陷阱,在这里可以找到更多的示例和解释。