为什么为匿名类生成的GetHashCode()实现中的初始哈希值取决于属性名称?
在为匿名类生成GetHashCode()
实现时,Roslyn根据属性名称计算初始哈希值。 例如,为其生成的类
var x = new { Int = 42, Text = "42" };
将有以下GetHashCode()
方法:
public override in GetHashCode() { int hash = 339055328; hash = hash * -1521134295 + EqualityComparer.Default.GetHashCode( Int ); hash = hash * -1521134295 + EqualityComparer.Default.GetHashCode( Text ); return hash; }
但是,如果我们更改属性名称,则初始值会更改:
var x = new { Int2 = 42, Text2 = "42" }; public override in GetHashCode() { int hash = 605502342; hash = hash * -1521134295 + EqualityComparer.Default.GetHashCode( Int2 ); hash = hash * -1521134295 + EqualityComparer.Default.GetHashCode( Text2 ); return hash; }
这种行为背后的原因是什么? 只选择一个大的[prime?]数字并将其用于所有匿名类都有问题吗?
只选择一个大的[prime?]数字并将其用于所有匿名类都有问题吗?
这样做没有任何问题,只会产生效率较低的价值。
GetHashCode
实现的目标是为不相等的值返回不同的结果。 当在基于散列的集合(例如Dictionary
)中使用这些值时,这会降低冲突的几率。
如果匿名值表示不同的类型,则永远不能等于另一个匿名值。 匿名值的类型由属性的形状定义:
- 物业名称
- 属性类型
- 财产数量
两个在任何这些特征上不同的匿名值表示不同的类型,因此永远不能是相等的值。
鉴于这是真的,编译器生成GetHashCode
实现是有意义的,这些实现往往会为不同类型返回不同的值。 这就是编译器在计算初始哈希时包含属性名称的原因。
除非罗斯林团队中有人加强,否则我们只能推测。 我会以同样的方式做到这一点。 对每个匿名类型使用不同的种子似乎是在哈希码中具有更多随机性的有用方法。 例如,它会导致new { a = 1 }.GetHashCode() != new { b = 1 }.GetHashCode()
为true。
我也想知道是否存在导致哈希码计算崩溃的坏种子。 我不这么认为。 即使0
种子也会起作用。
Roslyn源代码可以在AnonymousTypeGetHashCodeMethodSymbol
找到。 初始哈希码值基于匿名类型名称的哈希值。