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

时间:2011-11-18 05:20:59

标签: c# .net reflection

以下代码生成46104728的输出:

using System;

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

但是这样做:

using System;

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

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

5 个答案:

答案 0 :(得分:42)

您已经遇到了您认为存在问题的问题,但是,如果您要在同一执行中查看他们的哈希码 ,您会发现他们是&#39 ;不是完全相同,而是依赖于他们的使用顺序:

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

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

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

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

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

我深入研究了源代码,发现Type.GetHashCode的实现被强制转移到MemberInfo.GetHashCode上,再次被强制转移到调用Object.GetHashCode的{​​{1}}上。

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

我通过使用两个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 的类,将根据实例调用Object.GetHashCode的顺序为实例分配一个看似可预测的哈希值。


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

GetHashCode

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

答案 1 :(得分: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();

运行1:

33156464
15645912
33156464
15645912

运行2-6:

45653674
41149443
45653674
41149443

运行7:

46104728
12289376
46104728
12289376

运行8:

37121646
45592480
37121646
45592480

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

答案 2 :(得分:1)

这是一个令人惊讶的结果,有一个相对简单的解释。

班级Type使用Equals的默认实施和GetHashCode的{​​{1}}。具体而言,object个实例在它们是相同实例(即在相同的存储器地址)时是相等的。类似地,当对象是同一个实例时,它们的哈希码将是相等的。

Type使用缓存,因此对于给定的类型,它将始终返回相同的实例,该实例模仿成员相等的行为,但它不是:

typeof

对于原始问题,此结果可以适用于任何未覆盖object.ReferenceEquals(typeof(string), typeof(string)) == true 的引用类型。没有理由GetHashCode的输出应该是随机的,它只需要对不同内存地址的对象(并且在输出范围内分布均匀)不同。如果从同一起始点顺序分配内存地址,则从这些对象生成的哈希代码序列也将相同。

我应该补充一点,我不知道GetHashCode的实际基本实现,我只是理论上认为它从引用类型的内存地址派生是明智的。

答案 3 :(得分:0)

正如另外的参考:

2个不同泛型类的HashCode是相同的:

List<int>.GetType().GetHashCode() == List<string>.GetType().GetHashCode()

拥有不同的HashCode:

List<int>.GetType().GetGenericTypeDefinition.GetHashCode() != List<string>.GetType().GetGenericTypeDefinition.GetHashCode()

如果你必须比较通用类......

答案 4 :(得分:0)

在回应Eric Ouellet的回答时,我甚至不会评论错误的语法(哎呀,猜猜我刚才做过),但这些信息实际上是不准确的。

Visual Studio中C#交互式控制台的结果证明GetHashCode()在泛型类型上按预期工作。

证人:

> typeof(List<int>).GetHashCode()
42194754
> typeof(List<string>).GetHashCode()
39774547
> typeof(Stack<string>).GetHashCode()
59652943
> typeof(Stack<int>).GetHashCode()
5669220