C# 当重写Equals方法时,为什么重写GetHashCode很重要?

C# 当重写Equals方法时,为什么重写GetHashCode很重要?,c#,overriding,hashcode,C#,Overriding,Hashcode,给定以下类 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;

给定以下类

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
表示
Foo
s表的一行。覆盖
GetHashCode
的首选方法是什么


为什么覆盖
GetHashCode
很重要?

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

方法应反映
等于
逻辑;这些规则是:

  • 如果两个值相等(
    Equals(…)==true
    ),则它们必须为
    GetHashCode()
  • 如果
    GetHashCode()
    相等,则它们不必相同;这是一个冲突,将调用
    Equals
    ,查看它是否为真正的相等
在本例中,看起来“
returnfooid;
”是一个合适的
GetHashCode()
实现。如果您正在测试多个属性,通常会使用如下代码将它们组合在一起,以减少对角冲突(即,
new Foo(3,5)
new Foo(5,3)
具有不同的哈希代码):


OH-为了方便起见,您也可以考虑提供<代码>=< /代码>和<代码>!重写

等于
GetHashCode
时的code>运算符



当您出错时会发生什么情况的一个演示是。

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

方法应反映
等于
逻辑;这些规则是:

  • 如果两个值相等(
    Equals(…)==true
    ),则它们必须为
    GetHashCode()
  • 如果
    GetHashCode()
    相等,则它们不必相同;这是一个冲突,将调用
    Equals
    ,查看它是否为真正的相等
在本例中,看起来“
returnfooid;
”是一个合适的
GetHashCode()
实现。如果您正在测试多个属性,通常会使用如下代码将它们组合在一起,以减少对角冲突(即,
new Foo(3,5)
new Foo(5,3)
具有不同的哈希代码):


OH-为了方便起见,您也可以考虑提供<代码>=< /代码>和<代码>!重写

等于
GetHashCode
时的code>运算符



当您出错时会发生什么的一个示例是。

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

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

通过重写Equals,您基本上是在说明您是更了解如何比较给定类型的两个实例的人,因此您可能是提供最佳哈希代码的最佳候选者

这是ReSharper如何为您编写GetHashCode()函数的示例:

public override int GetHashCode()
{
    unchecked
    {
        var result = 0;
        result = (result * 397) ^ m_someVar1;
        result = (result * 397) ^ m_someVar2;
        result = (result * 397) ^ m_someVar3;
        result = (result * 397) ^ m_someVar4;
        return result;
    }
}

正如您所看到的,它只是试图根据类中的所有字段猜测一个好的哈希代码,但由于您知道对象的域或值范围,因此仍然可以提供更好的哈希代码。

通过覆盖Equals,您基本上是在说明您是更了解如何比较给定类型的两个实例的人,因此,您可能是提供最佳哈希代码的最佳候选者

这是ReSharper如何为您编写GetHashCode()函数的示例:

public override int GetHashCode()
{
    unchecked
    {
        var result = 0;
        result = (result * 397) ^ m_someVar1;
        result = (result * 397) ^ m_someVar2;
        result = (result * 397) ^ m_someVar3;
        result = (result * 397) ^ m_someVar4;
        return result;
    }
}

正如您所看到的,它只是试图根据类中的所有字段猜测一个好的哈希代码,但由于您知道对象的域或值范围,因此仍然可以提供一个更好的。

实际上很难正确实现
GetHashCode()
,因为除了前面提到的规则之外,哈希代码在对象的生存期内不应更改。因此,用于计算哈希代码的字段必须是不可变的

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

实际上很难正确实现
GetHashCode()
,因为除了前面提到的规则之外,哈希代码在对象的生存期内不应该更改。因此,用于计算哈希代码的字段必须是不可变的

当我与NHibernate合作时,我终于找到了这个问题的解决方案。 我的方法是根据对象的ID计算哈希代码。ID只能通过构造函数设置,因此如果y
public override int GetHashCode()
{
    return string.Format("{0}_{1}_{2}", prop1, prop2, prop3).GetHashCode();
}
   public override int GetHashCode()
   {
      return base.GetHashCode();
   }

   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
      }
   }

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

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

    return fooItem.FooId == this.FooId;
}
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 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;  
    }