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

标签 c# gethashcode

来自 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.

I got bitten by this today when I was using a KeyValuePair as a key in a Dictionary (it stored xml attribute name (enum) and it's value (string)), and expected for it to have it's hashcode computed based on all its fields, but according to implementation it only considered the Key part.

Example (c/p from Linqpad):

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。

如果测试失败,它会执行慢速版本,在道德上等同于反射(reflection)。这就是您得到的,您的 KeyValuePair<> 包含引用。而这个只检查第一个候选字段,就像评论说的那样。这肯定是性能优化,避免浪费太多时间。

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

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

关于c# - 为什么 ValueType.GetHashCode() 是这样实现的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3841602/

相关文章:

c# - CompreTo() 中的随机数与 GetHashCode() 的对比?

c# - 比较两个对象时有条件地更改 GetHashCode()

c# - 如何在 XmlDocument 中添加多个不同的前缀属性

c# - 杀死远程机器上的进程

c# - 为没有字段的值对象覆盖 GetHashCode()

c# - 重写 GetHash 代码和 Equals 中断绑定(bind)

.net-4.0 - 为什么这些哈希码相等?

c# - 在 C# 中加入两个条件

c# - 在 CentOS 7 上将 System.IO.Compression 程序集添加到 Mono

c# - 反射(reflection) |发射|动态资源生成 - 使用 IResourceWriter.AddResource(key,value) 添加的资源值无法读取