簡體   English   中英

覆蓋GetHashCode()

[英]Overriding GetHashCode()

本文中 ,Jon Skeet提到他通常使用這種算法來覆蓋GetHashCode()

public override int GetHashCode()
{
  unchecked // Overflow is fine, just wrap
  {
    int hash = 17;
    // Suitable nullity checks etc, of course :)
    hash = hash * 23 + Id.GetHashCode();
    return hash;
  }
}

現在,我嘗試使用它,但Resharper告訴我,方法GetHashCode()應該只使用只讀字段進行散列(盡管編譯很好)。 什么是好的做法,因為現在我真的不能讓我的字段是只讀的?

我嘗試通過Resharper生成這個方法,這是結果。

public override int GetHashCode()
{
  return base.GetHashCode();
}

這沒什么貢獻,說實話......

如果您的所有字段都是可變的並且您必須實現GetHashCode方法,那么我擔心這是您需要的實現。

public override int GetHashCode() 
{ 
    return 1; 
} 

是的,這是低效的,但這至少是正確的。

問題是Dictionary和HashSet集合正在使用GetHashCode將每個項目放在存儲桶中。 如果基於某些可變字段計算哈希碼,並且在將對象放入HashSet或Dictionary后實際更改了字段,則無法再從HashSet或Dictionary中找到該對象。

請注意,對於返回相同HashCode 1的所有對象,這基本上意味着所有對象都放在HashSet或Dictionary中的同一個桶中。 因此,HashSet或Dictionary中始終只有一個存儲桶。 嘗試查找對象時,它將對唯一存儲桶中的每個對象執行相等檢查。 這就像在鏈表中進行搜索一樣。

有人可能會爭辯說,如果我們可以確保在將對象添加到HashCode或Dictionary集合后永遠不會更改字段,那么基於可變字段實現哈希碼就可以了。 我個人認為這很容易出錯。 兩年后有人接管你的代碼可能不會意識到這一點並且意外地破壞了代碼。

請注意,您的GetHashCode必須與您的Equals方法齊頭並進。 如果你可以只使用引用相等(當你的類中沒有兩個不同的實例可以相等時),那么你可以安全地使用繼承自Object的Equals和GetHashCode。 這比僅僅從GetHashCode return 1要好得多。

我個人傾向於在沒有不可變字段的類中為GetHashCode()每個實現返回不同的數值。 這意味着如果我有一個包含不同實現類型的字典,則有可能將不同類型的不同實例放在不同的存儲區中。

例如

public class A
{
    // TODO Equals override

    public override int GetHashCode()
    {
        return 21313;
    } 
}

public class B
{
    // TODO Equals override

    public override int GetHashCode()
    {
        return 35507;
    } 
}

然后,如果我有一個Dictionary<object, TValue>包含AB和其他類型的實例,那么查找的性能將比GetHashCode所有實現返回相同的數值更好。

還應該注意的是,我使用素數來獲得更好的分布。

根據評論我在這里提供了一個LINQPad示例它演示了為不同類型使用return 1和為每種類型返回不同值之間的性能差異。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM