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