为什么为匿名类生成的GetHashCode()实现中的初始哈希值取决于属性名称?

时间:2015-09-27 13:44:12

标签: c# roslyn

在为匿名类生成GetHashCode()实现时,Roslyn根据属性名计算初始哈希值。例如,为

生成的类
var x = new { Int = 42, Text = "42" };

将采用以下GetHashCode()方法:

public override in GetHashCode()
{
   int hash = 339055328;
   hash = hash * -1521134295 + EqualityComparer<int>.Default.GetHashCode( Int );
   hash = hash * -1521134295 + EqualityComparer<string>.Default.GetHashCode( Text );
   return hash;
}

但是如果我们更改属性名称,则初始值会更改:

var x = new { Int2 = 42, Text2 = "42" };

public override in GetHashCode()
{
   int hash = 605502342;
   hash = hash * -1521134295 + EqualityComparer<int>.Default.GetHashCode( Int2 );
   hash = hash * -1521134295 + EqualityComparer<string>.Default.GetHashCode( Text2 );
   return hash;
}

这种行为背后的原因是什么?选择一个大的[prime?]数字并将其用于所有匿名类是否有问题?

2 个答案:

答案 0 :(得分:6)

  

选择一个大的[prime?]数字并将其用于所有匿名类是否有问题?

这样做没有错,它往往会产生效率较低的价值。

GetHashCode实现的目标是为不相等的值返回不同的结果。当在基于散列的集合(例如Dictionary<TKey, TValue>)中使用这些值时,这可以减少冲突的几率。

如果匿名值代表不同的类型,则永远不能等于另一个匿名值。匿名值的类型由属性的形状定义:

  • 物业名称
  • 属性类型
  • 属性数

两个不同的匿名值表示不同的类型,因此永远不能是相等的值。

鉴于这是真的,编译器生成GetHashCode实现有意义,这些实现往往会为不同类型返回不同的值。这就是the compiler在计算初始哈希时包含属性名称的原因。

答案 1 :(得分:4)

除非罗斯林团队的某些人加强,否则我们只能推测。我会以同样的方式做到这一点。对每个匿名类型使用不同的种子似乎是在哈希码中具有更多随机性的有用方法。例如,它会导致new { a = 1 }.GetHashCode() != new { b = 1 }.GetHashCode()为真。

我也想知道是否存在导致哈希码计算崩溃的坏种子。我不这么认为。即使是0种子也可以。

可以在AnonymousTypeGetHashCodeMethodSymbol中找到Roslyn源代码。初始哈希码值基于匿名类型名称的哈希值。