给定以下类别

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


当前回答

怎么样:

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

假设性能不是问题:)

其他回答

是的,如果您的项将用作字典或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;
}

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


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

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

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

这并不一定重要;这取决于集合的大小和性能要求,以及您的类是否将用于您可能不知道性能要求的库中。我经常知道我的集合大小不是很大,我的时间比创建一个完美的哈希代码所获得的几微秒的性能更有价值;所以(为了消除编译器发出的恼人警告)我只需使用:

   public override int GetHashCode()
   {
      return base.GetHashCode();
   }

(当然,我也可以使用#pragma关闭警告,但我更喜欢这种方式。)

当然,当你处于一个你确实需要表现的位置时,这里其他人提到的所有问题都适用。最重要的是,否则在从哈希集或字典中检索项目时会得到错误的结果:哈希码不能随对象的生存时间而变化(更准确地说,在需要哈希码的时间,例如在字典中作为关键字时):例如,以下内容是错误的,因为Value是公共的,因此可以在实例的生命周期内在类外部进行更改,因此不能将其用作哈希代码的基础:


   class A
   {
      public int Value;

      public override int GetHashCode()
      {
         return Value.GetHashCode(); //WRONG! Value is not constant during the instance's life time
      }
   }    

另一方面,如果无法更改值,则可以使用:


   class A
   {
      public readonly int Value;

      public override int GetHashCode()
      {
         return Value.GetHashCode(); //OK  Value is read-only and can't be changed during the instance's life time
      }
   }

重写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