2015-09-27 50 views
5

當爲匿名類生成GetHashCode()實現時,Roslyn會根據屬性名稱計算初始哈希值。例如,對於爲什麼爲匿名類生成的GetHashCode()實現中的初始哈希值取決於屬性名稱?

生成的類
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?]數字並將其用於所有匿名類,是否存在一些問題?

+0

如果您使用不同的常量,以使不同類型的對象「更」不同,對我來說似乎更有用。 – usr

回答

6

僅僅挑選一個大的[prime?]數字並將其用於所有匿名類都有問題嗎?

這樣做沒有什麼不對,它往往會產生一個效率較低的值。

GetHashCode實現的目標是針對不相等的值返回不同的結果。這可以降低在基於散列的集合中使用值時發生衝突的機率(如Dictionary<TKey, TValue>)。

如果匿名值代表不同的類型,它永遠不會等於另一個匿名值。類型的匿名值是由屬性的形狀限定:

  • 屬性名稱
  • 類型屬性的
  • 計數其上的任何的不同性質

兩個匿名值的這些特徵代表不同的類型,因此永遠不可能是相同的值。

鑑於這是真的,編譯器生成GetHashCode實現往往會返回不同類型的值,這是有意義的。這就是爲什麼the compiler在計算初始散列時包含屬性名稱的原因。

+0

我明白了。我猜如果你不管什麼原因把不同類型的實例放入'HashSet ',那麼具有不變初始值的哈希碼就會炸燬你的臉。 – HellBrick

+0

但是,如果爲不同類型的對象區分哈希通常是一個好主意,爲什麼默認情況下匿名類是唯一的情況?我還沒有在BCL中找到過這樣一個例子。例如,Int64,TimeSpan和DateTime基本上共享實現和'0L.GetHashCode()'== TimeSpan.Zero.GetHashCode()== ==''default(DateTime).GetHashCode()'。 – HellBrick

+0

@HellBrick只有在可能的值插入到相同的散列結構中時,區分纔有意思。實際上,不同類型的匿名值實際上並不罕見。但是,如果有的話,哈希結構可能同時具有'DateTime'和'TimeSpan'值作爲關鍵字,這可能非常罕見。 – JaredPar

4

除非有人來自羅斯林團隊,否則我們只能推測。我會以同樣的方式做到這一點。爲每個匿名類型使用不同的種子似乎是在散列碼中具有更多隨機性的有用方法。例如,它導致new { a = 1 }.GetHashCode() != new { b = 1 }.GetHashCode()爲真。

我還想知道是否有任何不良種子導致哈希碼計算崩潰。我不這麼認爲。即使是0種子也會起作用。

Roslyn源代碼可以在AnonymousTypeGetHashCodeMethodSymbol找到。初始哈希碼值基於匿名類型名稱的散列。

相關問題