C# 为什么GetHashCode应该实现与Equals相同的逻辑?
在第页,它说: 警告: 如果重写GetHashCode方法,则还应重写Equals,反之亦然。如果在测试两个对象是否相等时,重写的Equals方法返回true,重写的GetHashCode方法必须为这两个对象返回相同的值 我也看到了许多类似的建议,我可以理解,当重写Equals方法时,我还希望重写GetHashCode。不过,据我所知,GetHashCode用于哈希表查找,这与等式检查不同 下面是一个例子来帮助解释我想问的问题:C# 为什么GetHashCode应该实现与Equals相同的逻辑?,c#,equality,gethashcode,C#,Equality,Gethashcode,在第页,它说: 警告: 如果重写GetHashCode方法,则还应重写Equals,反之亦然。如果在测试两个对象是否相等时,重写的Equals方法返回true,重写的GetHashCode方法必须为这两个对象返回相同的值 我也看到了许多类似的建议,我可以理解,当重写Equals方法时,我还希望重写GetHashCode。不过,据我所知,GetHashCode用于哈希表查找,这与等式检查不同 下面是一个例子来帮助解释我想问的问题: public class Temperature /* Immut
public class Temperature /* Immutable */
{
public Temperature(double value, TemperatureUnit unit) { ... }
private double Value { get; set; }
private TemperatureUnit Unit { get; set; }
private double GetValue(TemperatureUnit unit)
{
/* return value converted into the specified unit */
}
...
public override bool Equals(object obj)
{
Temperature other = obj as Temperature;
if (other == null) { return false; }
return (Value == other.GetValue(Unit));
}
public override int GetHashCode()
{
return Value.GetHashCode() + Unit.GetHashCode();
}
}
在本例中,两个温度对象被视为相等,即使它们内部存储的东西不同(例如295.15 K==22摄氏度)。此时,GetHashCode方法将为每个方法返回不同的值。这两个对象是相等的,但它们也不相同,因此它们具有不同的哈希代码,这不正确吗?当在哈希表中存储值时,例如
字典
,框架将首先调用GetHashCode()
,并检查哈希表中是否已经存在该哈希代码的存储桶。如果存在,它将调用.Equals()
,查看新值是否确实等于现有值。如果不是(这意味着两个对象不同,但会产生相同的哈希代码),则称为冲突。在这种情况下,此存储桶中的项目存储为链表,检索某个值将变为O(n)
如果您实现了GetHashCode()
,但没有实现Equals()
,框架将求助于使用引用相等来检查相等,这将导致每个实例都创建冲突
如果您实现了Equals()
,但没有实现GetHashCode()
,则可能会遇到这样的情况:您有两个相等的对象,但会产生不同的哈希代码,这意味着它们会在哈希表中保留各自的值。这可能会使任何使用您的类的人感到困惑
至于什么样的物体被认为是平等的,这取决于你。如果我创建一个基于温度的哈希表,我应该能够使用它的摄氏或华氏值来引用同一个项目吗?如果是这样,它们需要产生相同的散列值,Equals()
需要返回true
更新:
让我们退一步,首先看看散列代码的用途。在此上下文中,散列代码被用作快速识别两个对象是否最有可能相等的方法。如果我们有两个具有不同散列码的对象,我们就知道它们不相等。如果我们有两个具有相同哈希代码的对象,我们知道它们很可能相等。我这么说很可能是因为int只能用来表示几十亿个可能的值,字符串当然可以包含查尔斯·狄更斯的全集,或者任何数量的可能值。NET框架中的许多内容都是基于这些事实,使用您的代码的开发人员将假定事情的工作方式与框架的其余部分一致
如果您有两个具有不同哈希代码的实例,但是有一个返回true的Equals()
实现,那么您就违反了这个约定。比较两个对象的开发人员可能会使用其中一个对象引用哈希表中的键,并期望从中获取现有值。如果哈希代码突然不同,则此代码可能会导致运行时异常。或者返回对完全不同对象的引用
295.15k和22C在你的课程范围内是否相等是你的选择(在我看来,它们不是)。但是,无论您决定什么,相等的对象都必须返回相同的has代码。在哈希表中存储值时,例如
字典
,框架将首先调用GetHashCode()
,并检查哈希表中是否已经存在该哈希代码的bucket。如果存在,它将调用.Equals()
,查看新值是否确实等于现有值。如果不是(这意味着两个对象不同,但会产生相同的哈希代码),则称为冲突。在这种情况下,此存储桶中的项目存储为链表,检索某个值将变为O(n)
如果您实现了GetHashCode()
,但没有实现Equals()
,框架将求助于使用引用相等来检查相等,这将导致每个实例都创建冲突
如果您实现了Equals()
,但没有实现GetHashCode()
,则可能会遇到这样的情况:您有两个相等的对象,但会产生不同的哈希代码,这意味着它们会在哈希表中保留各自的值。这可能会使任何使用您的类的人感到困惑
至于什么样的物体被认为是平等的,这取决于你。如果我创建一个基于温度的哈希表,我应该能够使用它的摄氏或华氏值来引用同一个项目吗?如果是这样,它们需要产生相同的散列值,Equals()
需要返回true
更新:
让我们退一步,首先看看散列代码的用途。在此上下文中,散列代码被用作快速识别两个对象是否最有可能相等的方法。如果我们有两个具有不同散列码的对象,我们就知道它们不相等。如果我们有两个具有相同哈希代码的对象,我们知道它们很可能相等。我之所以这么说是因为int只能用来表示几十亿个可能的值,而字符串当然可以包含
public class Temperature
{
public Temperature(double value, TemperatureUnit unit) {
Value = ConvertValue(value, unit, TemperatureUnit.Celsius);
}
private double Value { get; set; }
private double ConvertValue(double value, TemperatureUnit originalUnit, TemperatureUnit targetUnit)
{
/* return value from originalUnit converted to targetUnit */
}
private double GetValue(TemperatureUnit unit)
{
return ConvertValue(value, TemperatureUnit.Celsius, unit);
}
public override bool Equals(object obj)
{
Temperature other = obj as Temperature;
if (other == null) { return false; }
return (Value == other.Value);
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
public override bool Equals(object obj)
{
Temperature other = obj as Temperature;
if (other == null) { return false; }
return (Value == other.Value && Unit == other.Unit);
}
public override int GetHashCode()
{
// note that the value returned from ConvertToSomeBaseUnit
// should probably be cached as a private member
// especially if your class is supposed to immutable
return Value.ConvertToSomeBaseUnit().GetHashCode();
}
public bool TemperaturesAreEqual(Temperature other)
{
if (other == null) { return false; }
return (Value == other.GetValue(Unit));
}