Object.GetHashCode()的实现

时间:2014-11-28 20:53:22

标签: c# object gethashcode

我正在阅读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()

1 个答案:

答案 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;