首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

Effective C# 原則 10:精通 GetHashCode() 缺陷與正確實作

     編輯:關於C#

GetHashCode() 僅用於雜湊容器(Hashtable、Dictionary)的鍵值物件。基類實作對參考型別正確但低效,對實值型別常錯誤。若型別不作鍵值,完全不用覆寫;若必須作鍵值,務必正确实作滿足三大鐵律。

三大鐵律

  1. 相等必同雜湊:a == b → a.GetHashCode() == b.GetHashCode()。
  2. 實例不變性:物件生命週期內 GetHashCode() 必須恆定回傳相同值。
  3. 均勻分佈:對所有輸入在 int 範圍均勻分佈,確保雜湊桶負載平衡。

基類實作缺陷分析

Object.GetHashCode() — 參考型別

  • 使用內部物件 ID(建構時遞增整數)。
  • 滿足規則 1、2(若未覆寫 ==)。
  • 違反規則 3:值集中於較小整數範圍,分佈極不均勻 → 雜湊效能差。
  • 若覆寫 == 則必須覆寫 GetHashCode(),否則違反規則 1。

ValueType.GetHashCode() — 實值型別

  • 回傳第一個欄位的雜湊碼。
  • 規則 1:僅當 == 也基於第一欄位比較時成立。
  • 規則 2:第一欄位必須不可變,否則雜湊碼隨狀態改變 → 物件在雜湊表中「丟失」。
  • 規則 3:取決於第一欄位分佈;若第一欄位常相同(如建立日期),分佈極差。

正确实作策略

核心原則:只使用不可變欄位參與雜湊計算

public class Customer
{
    private readonly string _name; // 不可變
    private decimal _revenue;      // 可變,不可參與雜湊
    public Customer(string name) { _name = name; }
    public string Name => _name;
    public Customer ChangeName(string newName) => new Customer(newName); // 不可變模式
    public override int GetHashCode() => _name.GetHashCode();
}

常用演算法:XOR 組合多欄位

public override int GetHashCode()
{
    unchecked // 避免溢位檢查效能損失
    {
        int hash = 17;
        hash = hash * 23 + _field1.GetHashCode();
        hash = hash * 23 + _field2.GetHashCode();
        // 排除可變欄位、事件、委派等
        return hash;
    }
}

實值型別必須是不可變型別

若實值型別作鍵值,整個結構必須不可變(參見原則 7)。否則使用者修改欄位會導致雜湊碼改變,物件在表中丟失;或因裝箱產生副本,修改副本不影響表中原物件,行為詭異難除錯。

總結

  • 非鍵值型別:勿覆寫,接受基類預設行為(知其缺陷即可)。
  • 鍵值型別:必須不可變,覆寫 GetHashCode() 滿足三大鐵律,同步覆寫 Equals() 與 ==(參見原則 9)。
  • 無萬用公式,需依型別語意設計;XOR/質數組合為常用基礎模式。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved