为什么System.Type.GetHashCode为所有实例和类型返回相同的值?

Mic*_*ray 42 .net c# reflection

以下代码生成46104728的输出:

using System;

namespace TestApplication
{
    internal static class Program
    {
        private static void Main()
        {
            Type type = typeof(string);
            Console.WriteLine(type.GetHashCode());
            Console.ReadLine();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但这样做:

using System;

namespace TestApplication
{
    internal static class Program
    {
        private static void Main()
        {
            Type type = typeof(Program);
            Console.WriteLine(type.GetHashCode());
            Console.ReadLine();
        }
    }
 }
Run Code Online (Sandbox Code Playgroud)

然而,在http://ideone.com上,它会为每种类型产生不同的结果.此问题现已在多个系统上重现.我现在正在使用.NET 4.0.

use*_*116 43

你已经遇到了你认为是一个问题,但是,如果你在同一个执行中查看他们的哈希码,你会发现它们不相同,而是依赖于它们的使用顺序:

Console.WriteLine("{0} {1:08X}", typeof(string), typeof(string).GetHashCode());
Console.WriteLine("{0} {1:08X}", typeof(Program), typeof(Program).GetHashCode());
// System.String 02BF8098
// Program 00BB8560
Run Code Online (Sandbox Code Playgroud)

如果我再次运行相同的程序,交换他们的顺序:

Console.WriteLine("{0} {1:08X}", typeof(Program), typeof(Program).GetHashCode());
Console.WriteLine("{0} {1:08X}", typeof(string), typeof(string).GetHashCode());
// Program 02BF8098
// System.String 00BB8560
Run Code Online (Sandbox Code Playgroud)

这在运行时不是问题,因为返回的值不违反实现规则Object.GetHashCode.

但是,正如你所说,这种行为似乎很好奇!

我深入研究了源代码,发现它的实现Type.GetHashCode被强制执行,MemberInfo.GetHashCode再次被强制转移到Object.GetHashCode哪个调用上RuntimeHelpers.GetHashCode(this).

在这一点上,路径变冷了,但是,我的假设是该方法的内部工作方式根据调用顺序创建一个新值,每个实例映射一次.

我通过运行上面的两个实例Program(在添加属性来识别它们之后)运行相同的代码来测试这个假设:

var b = new Program() { Name = "B" };
var a = new Program() { Name = "A" };
Console.WriteLine("{0} {1:08X}", a.Name, a.GetHashCode());
Console.WriteLine("{0} {1:08X}", b.Name, b.GetHashCode());
// A 02BF8098
// B 00BB8560
Run Code Online (Sandbox Code Playgroud)

因此,对于未明确覆盖的类Object.GetHashCode,将根据实例调用的顺序为实例分配一个看似可预测的哈希值GetHashCode.


更新:我去了解Rotor/Shared Source CLI如何处理这种情况,我了解到默认实现计算并在对象实例的同步块中存储哈希代码,从而确保哈希代码只生成一次.此哈希代码的默认计算是微不足道的,并使用每线程种子(包装是我的):

// ./sscli20/clr/src/vm/threads.h(938)
// 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).
Run Code Online (Sandbox Code Playgroud)

因此,如果实际的CLR遵循此实现,那么在对象的哈希码值中看到的任何差异都基于创建该实例的AppDomain和Managed Thread.


lep*_*pie 8

程序(.NET 4,AnyCPU):

var st = typeof(string);
var pt = typeof(Program);
Console.WriteLine(st.GetHashCode());
Console.WriteLine(pt.GetHashCode());
Console.WriteLine(typeof(string).GetHashCode());
Console.WriteLine(typeof(Program).GetHashCode());
Console.ReadLine();
Run Code Online (Sandbox Code Playgroud)

运行1:

33156464
15645912
33156464
15645912
Run Code Online (Sandbox Code Playgroud)

运行2-6:

45653674
41149443
45653674
41149443
Run Code Online (Sandbox Code Playgroud)

运行7:

46104728
12289376
46104728
12289376
Run Code Online (Sandbox Code Playgroud)

运行8:

37121646
45592480
37121646
45592480
Run Code Online (Sandbox Code Playgroud)

虽然只要哈希码在程序生命周期内是一致的,我就可以理解随机性,但令我困惑的是它并不总是随机的.

  • @JNZ:答案是`Type`不会覆盖`GetHashCode`因此使用默认的实现,基于在`GetHashCode`被调用的顺序上为它们分配一个伪确定性的方式. (3认同)
  • @JNZ:你应该给另一个回答者'滴答'.我只是我自己的观察而没有试图回答这个问题:) (2认同)