Object.GetHashCode()对引用或值是唯一的吗?

时间:2008-08-29 15:54:16

标签: c# .net

Object.GetHashCode()上的MSDN文档描述了该方法应如何工作的3条矛盾规则。

  1. 如果两个相同类型的对象表示相同的值,则哈希函数必须为任一对象返回相同的常量值。
  2. 为获得最佳性能,哈希函数必须为所有输入生成随机分布。
  3. 无论对对象进行任何更改,哈希函数都必须返回完全相同的值。
  4. 规则1& 3对我来说是矛盾的。

    Object.GetHashCode()是否根据对象的或对象的引用返回唯一编号。如果我覆盖该方法,我可以选择使用什么,但是如果有人知道的话,我想知道内部使用的是什么。

6 个答案:

答案 0 :(得分:29)

  

规则1& 3对我来说是矛盾的。

在某种程度上,他们是。原因很简单:如果对象存储在哈希表中,并且通过更改其值,您更改其哈希值,则哈希表已丢失该值,并且您无法通过查询哈希表再次找到它。重要的是,当对象存储在哈希表中时,它们会保留其哈希值。

要实现这一点,通常最简单的方法是使可清除对象不可变,从而避免整个问题。但是,只有那些字段不可变才能确定哈希值。

考虑以下示例:

struct Person {
    public readonly string FirstName;
    public readonly string Name;
    public readonly DateTime Birthday;

    public int ShoeSize;
}

人们很少改变他们的生日,大多数人都不会改变他们的名字(除非结婚时)。然而,他们的鞋子尺寸可能会随意增长,甚至会缩小。因此,使用他们的生日和名字而不是他们的鞋子大小来识别人是合理的。哈希值应该反映这一点:

public int GetHashCode() {
    return FirstName.GetHashCode() ^ Name.GetHashCode() ^ Birthday.GetHashCode();
}

答案 1 :(得分:5)

不确定您所指的MSDN文档。查看Object.GetHashCode(http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx)上的当前文档提供了以下“规则”:

  • 如果两个对象比较相等,则每个对象的GetHashCode方法必须返回相同的值。但是,如果两个对象的比较不相等,则两个对象的GetHashCode方法不必返回不同的值。

  • 对象的GetHashCode方法必须始终返回相同的哈希码,只要对对象状态没有修改即可确定对象的Equals方法的返回值。请注意,这仅适用于当前应用程序的执行,并且如果再次运行应用程序,则可以返回不同的哈希代码。

  • 为获得最佳性能,哈希函数必须为所有输入生成随机分布。

如果您指的是第二个项目符号点,则此处的关键短语是“只要没有对对象状态进行修改”和“仅对应用程序的当前执行为真”。

同样来自文档,

  

哈希函数用于快速生成与对象的对应的数字(哈希码)。散列函数通常特定于每个类型,并且必须至少使用一个实例字段作为输入。 [增加的重点是我的。]

至于实际实现,它明确指出派生类可以遵循Object.GetHashCode实现当且仅当派生类将值相等定义为引用相等且类型不是值类型。换句话说,Object.GetHashCode的默认实现将基于引用相等,因为不存在要使用的实例字段,因此不保证不同对象的唯一返回值。否则,您的实现应该特定于您的类型,并且应该至少使用一个实例字段。例如,String.GetHashCode的实现为相同的字符串值返回相同的哈希码,因此如果两个String对象表示相同的字符串值,则返回相同的哈希码,并使用字符串中的所有字符生成该哈希值。

答案 2 :(得分:4)

规则1& 3并不是真正的矛盾。

对于引用类型,哈希代码是从对象的引用派生的 - 更改对象的属性,引用是相同的。

对于值类型,哈希码是从值派生的,更改值类型的属性,然后获得值类型的全新实例。

答案 3 :(得分:1)

有关如何处理GetHashCode(超出Microsoft规则)的非常好的解释在Eric Lipperts(C#的设计者)博客中提供了文章" Guidelines and rules for GetHashCode" 。这里添加超链接是不好的做法(因为它们可能无效),但这个是值得的,并且如果超链接丢失,上面的信息可能仍会找到它。

答案 4 :(得分:0)

默认情况下,它基于对象的引用来完成它,但这意味着它是完全相同的对象,因此两者都将返回相同的哈希。但是散列应该基于值,就像字符串类一样。 “a”和“b”将具有不同的散列,但“a”和“a”将返回相同的散列。

答案 5 :(得分:0)

我无法确定Object.GetHashCode是如何在真正的 .NET Framework中实现的,但在Rotor中它将对象的SyncBlock索引用作哈希码。网上有一些关于它的博客文章,但大多数都来自2005年。