我正在阅读Effective C#,而且我对 Object.GetHashCode()
的评论不明白:
Object.GetHashCode()
使用System.Object
类中的内部字段来生成哈希值。创建的每个对象在创建时都会分配一个唯一的对象键,存储为整数 这些键从1开始,每次获得任何类型的新对象时都会递增 创建。对象标识字段在System.Object
构造函数中设置,以后不能修改。Object.GetHashCode()
将此值作为给定对象的哈希码返回。
我试着查看Object.GetHashCode()
的文档,但没有找到任何相关信息。
我编写了一段简单的代码来打印新生成的对象的哈希码:
using System;
namespace TestGetHashCode
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 100; i++)
{
object o = new object();
Console.WriteLine(o.GetHashCode());
}
}
}
}
打印的前几个数字是:
37121646,
45592480,
57352375,
2637164,
41014879,
3888474,
25209742,
26966483,
31884011
哪个似乎不适合
这些键从1开始,每次创建任何类型的新对象时都会递增...
Object.GetHashCode()
返回此值
然后,为了在System.Object
中找到此“内部字段”,我尝试使用ReSharper decompiled sources,但我找到的代码是
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
[__DynamicallyInvokable]
public virtual int GetHashCode()
{
return RuntimeHelpers.GetHashCode(this);
}
再次使用反编译源我发现RuntimeHelpers.GetHashCode
已实现为
[SecuritySafeCritical]
[__DynamicallyInvokable]
[MethodImpl(MethodImplOptions.InternalCall)]
public static int GetHashCode(object o);
在the MethodImpl attribute之后,似乎我无法查看实现,这对我来说是一个死胡同。
有人可以解释作者的评论(第一个引用)吗?
Object类中的内部字段是什么以及它如何用于实现Object.GetHashCode()
?
答案 0 :(得分:17)
好的,我最好写一下。这本书非常不准确。 Object.GetHashCode()的值在CLR内部生成,并且只要第一次调用GetHashCode(),就会按需计算。我引用了SSCLI20发行版中的代码,clr / src / vm / thread.h具有生成数字的函数,它看起来像这样(为了便于阅读而编辑):
inline DWORD GetNewHashCode()
{
// Every thread has its own generator for hash codes so that we won't get into a
// situation where two threads consistently give out the same hash codes.
// Choice of multiplier guarantees period of 2**32
// see Knuth Vol 2 p16 (3.2.1.2 Theorem A).
DWORD multiplier = m_ThreadId*4 + 5;
m_dwHashCodeSeed = m_dwHashCodeSeed*multiplier + 1;
return m_dwHashCodeSeed;
}
之后,它存储在对象的所谓同步块中,以便后续调用返回相同的值。实际存储的32位中只有26位,同步块需要一些状态位空间。仍然足够好以产生非常高质量的哈希码,碰撞是非常罕见的。
该代码中m_ThreadId变量的存在可以使用解释。为每个单独的线程存储随机数生成器种子。避免不得不锁定的诀窍。
m_dwHashCodeSeed在Thread构造函数中初始化,如下所示:
// Initialize this variable to a very different start value for each thread
// Using linear congruential generator from Knuth Vol. 2, p. 102, line 24
dwHashCodeSeed = dwHashCodeSeed * 1566083941 + 1;
m_dwHashCodeSeed = dwHashCodeSeed;
使用:
static DWORD dwHashCodeSeed = 123456789;