关于如何正确覆盖object.GetHashCode()的一般建议和指南

Svi*_*ish 44 .net c# hashcode gethashcode

根据MSDN,散列函数必须具有以下属性:

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

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

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


我一直在以下场景中找到自己:我创建了一个类,实现IEquatable<T>并重写了object.Equals(object).MSDN声明:

重写Equals的类型也必须覆盖GetHashCode; 否则,Hashtable可能无法正常工作.

然后它通常会为我停止一点.因为,你如何正确覆盖object.GetHashCode()?从来没有真正知道从哪里开始,这似乎是很多陷阱.

在StackOverflow中,有很多与GetHashCode重写相关的问题,但大多数问题似乎都是针对非常特殊的情况和具体问题.因此,我想在这里得到一个很好的汇编.概述与一般建议和指南.该做什么,不该做什么,常见的陷阱,从哪里开始,等等.

我希望它特别针对C#,但我认为它对其他.NET语言也有同样的作用(?).


我想也许最好的方法是每个主题创建一个答案,首先是快速简短的答案(如果可能的话,尽可能接近单行),然后可能会有更多信息,并以相关问题,讨论,博客文章等结束. ,如果有的话.然后,我可以创建一个帖子作为接受的答案(将其置于顶部),只需一个"目录".尽量保持简洁明了.而且不要只链接到其他问题和博客文章.尝试采用它们的本质,然后链接到源(特别是因为源可能会消失.另外,请尝试编辑和改进答案,而不是创建许多非常相似的答案.

我不是一个非常优秀的技术作家,但我至少会尝试格式化答案,使它们看起来很相似,创建目录等.我也会尝试在这里搜索一些相关的问题来回答部分问题.这些并且可能拉出我能管理的那些的本质.但由于我在这个主题上不是很稳定,所以我会尽量远离这个主题:p

Svi*_*ish 9

目录


我希望涵盖的内容,但尚未完成:

  • 如何创建整数(如何将对象"转换"为int对我来说不是很明显).
  • 基于哈希代码的字段.
    • 如果它只应该在不可变字段上,那么如果只有可变字段呢?
  • 如何生成一个好的随机分布.(MSDN Property#3)
    • 在这方面,似乎选择了一个很好的魔术素数(已经看过使用了17,23和397),但是你如何选择它,它究竟是什么呢?
  • 如何确保哈希代码在整个对象生存期内保持不变.(MSDN Property#2)
    • 特别是当相等性基于可变字段时.(MSDN Property#1)
  • 如何处理复杂类型的字段(不在内置的C#类型中).
    • 复杂对象和结构,数组,集合,列表,字典,泛型类型等.
    • 例如,即使列表或字典可能只读,但这并不意味着它的内容.
  • 如何处理继承的类.
    • 你应该以某种方式加入base.GetHashCode()你的哈希码?
  • 你在技术上可能只是懒惰并返回0吗?将严重破坏MSDN准则号#3,但至少会确保#1和#2始终为真:P
  • 常见的陷阱和陷阱.


Svi*_*ish 7

在GetHashCode实现中常见的那些神奇数字是什么?

他们是素数.素数用于创建哈希码,因为素数最大化了哈希码空间的使用.

具体来说,从小素数3开始,只考虑结果的低阶nybbles:

  • 3*1 = 3 = 3(mod 8)= 0011
  • 3*2 = 6 = 6(mod 8)= 1010
  • 3*3 = 9 = 1(mod 8)= 0001
  • 3*4 = 12 = 4(mod 8)= 1000
  • 3*5 = 15 = 7(mod 8)= 1111
  • 3*6 = 18 = 2(mod 8)= 0010
  • 3*7 = 21 = 5(mod 8)= 1001
  • 3*8 = 24 = 0(mod 8)= 0000
  • 3*9 = 27 = 3(mod 8)= 0011

我们重新开始.但是你会注意到,在开始重复之前,我们的素数的连续倍数在我们的nybble中生成了每个可能的位排列.我们可以使用任何素数和任意数量的位获得相同的效果,这使得素数最适合生成近似随机哈希码.我们通常在上面的例子中看到较大的素数而不是像3这样的小素数的原因是,对于我们的哈希码中更大的比特数,使用小素数得到的结果甚至不是伪随机的 - 它们只是一个增加序列直到遇到溢出.为了获得最佳随机性,应使用导致相当小系数溢出的素数,除非您可以保证系数不会很小.

相关链接: