我需要覆盖 GetHashCode() 方法吗?对于引用类型而言。

23

我阅读了关于GetHashCode的大多数StackOverflow问题,但我仍不确定在引用类型上是否必须重写GetHashCode。我从其他问题中某个人的答案中找到了以下内容:

Object.GetHashCode()使用System.Object类中的内部字段生成哈希值。每个创建的对象在创建时都分配一个唯一的对象键,作为整数存储。这些键从1开始,并在创建任何类型的新对象时递增。

如果这在.NET Framework 3.5中仍然是正确的(可以请有人确认吗?),那么默认实现在引用类型上唯一的问题就是哈希码分布不好。

我会分开提问:

a)所以如果在Dictionary中使用它,建议重写GetHashCode,还是默认实现表现良好?

b)我的引用类型很容易做到这一点,因为它们具有唯一标识它们的字段,但是对于所有成员也是引用类型的引用类型该怎么办?

2个回答

20

如果您重写了Object.Equals(),那么在引用类型上只有需要重写GetHashCode()方法。

这是因为通常情况下,2个引用将始终是不同的(a.Equals(b)==false,除非它们是同一个对象)。在此情况下,GetHashCode()的默认实现将提供2个不同的哈希值,所以一切都好。

但是,如果您重写了Equals()方法,则不能保证这种行为。如果两个对象相等(根据Equals()的定义),则需要确保它们使用GetHashCode()生成相同的哈希码,因此您应该重写它。


只是一点小提示,如果你在引用类型上重写 GetHashCode 方法,最好确保该对象是不可变的。否则,如果其内容发生更改,你可能会在 HashTable 中丢失它。 - David Klempfner

0

我刚刚做了一个样本测试,但我不明白它是如何从1开始递增的。

for (int i = 0; i < 16; i++)
{
    object obj = new object();
    Console.Write(obj.GetHashCode() + " ");
}

带着这些结果:

45653674 41149443 39785641 45523402 35287174 44419000 52697953 22597652 
10261382 59109011 42659827 40644060 17043416 28756230 18961937 47980820

事实上,使用反编译器,我只能看到这个:
internal static extern int InternalGetHashCode(object obj);

关于它具体是如何发生的,对我来说仍然是个谜(也许有一些模式,但我现在不打算深究——也许是某种“伪随机数” 算法?)。CLR团队中的某个人可能能回答这个问题。

至于其他问题,里德实际上比我先回答了GetHashCodeEquals。MSDN页面用更详细的描述说明了它,以防万一。


这要看情况...在.NET 1.1和1.0中,我们有不同于.NET 2.0的算法。如果我没记错的话,2.0中是一个随机生成器。 - boj
是的,我没有看到它在任何早期版本中的外观。我只是测试了他所问的3.5版本。是的,我认为这是一个随机生成器(因此我链接了一个)。 - Erich Mirabal
1
InternalGetHashCode在CLR中映射到ObjectNative::GetHashCode函数,该函数的内容可以在此文章的顶部答案中看到。 - Erwin Mayer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接