为什么 ValueType.GetHashCode() 是这样实现的?

2023-11-26

From ValueType.cs



**Action: Our algorithm for returning the hashcode is a little bit complex. We look 
**        for the first non-static field and get it's hashcode.  If the type has no 
**        non-static fields, we return the hashcode of the type. We can't take the
**        hashcode of a static member because if that member is of the same type as 
**        the original type, we'll end up in an infinite loop.
  

今天,当我使用 KeyValuePair 作为字典中的键(它存储 xml 属性名称(枚举)及其值(字符串))时,我被这个问题困扰了,并期望它根据其所有字段计算其哈希码,但根据实现,它只考虑了关键部分。

示例(来自 Linqpad 的 c/p):

void Main()
{
    var kvp1 = new KeyValuePair<string, string>("foo", "bar");
    var kvp2 = new KeyValuePair<string, string>("foo", "baz");

    // true
    (kvp1.GetHashCode() == kvp2.GetHashCode()).Dump();
}

我猜第一个非静态字段意味着声明顺序中的第一个字段,无论出于何种原因更改源中的变量顺序,并且相信它不会在语义上更改代码,这也可能会导致麻烦。


ValueType.GetHashCode() 的实际实现与注释不太匹配。它有两个版本的算法,快速和慢速。它首先检查结构是否包含引用类型的任何成员以及字段之间是否有任何填充。填充是结构体值中的空白区域,是在 JIT 编译器对齐字段时创建的。包含 bool 和 int (3 个字节)的结构中有填充,但包含 int 和 int 时没有填充,它们紧密地组合在一起。

没有引用并且没有填充,它可以执行快速版本,因为结构值中的每一位都是属于字段值的位。它一次只是异或 4 个字节。您将获得考虑所有成员的“良好”哈希代码。 .NET 框架中的许多简单结构类型都以这种方式运行,例如 Point 和 Size。

如果测试失败,它就会执行慢速版本,道德上相当于反思。这就是您得到的,您的 KeyValuePair 包含引用。正如评论所说,这个只检查第一个候选字段。这无疑是一种性能优化,避免浪费太多时间。

是的,令人讨厌的细节并不广为人知。当有人注意到他们的收集代码很烂时,通常会发现这一点。

另一个令人痛苦的细节是:快速版本有一个错误,当结构包含十进制类型的字段时,该错误会字节化。值 12m 和 12.0m 在逻辑上相等,但它们不具有相同的位模式。 GetHashCode() 会说它们不相等。哎哟。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 ValueType.GetHashCode() 是这样实现的? 的相关文章

随机推荐