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

Effective C# 原則 10:瞭解 GetHashCode() 的缺陷(完)

     編輯:關於C語言

可變型別作雜湊鍵的災難

var c1 = new Customer("Acme Products");
map.Add(c1, orders);

// 稍後修改名稱
c1.Name = "Acme Software"; // GetHashCode() 改變!

// 現在找不到 c1 了!
// c1 存放在 "Acme Products" 桶,但現在雜湊值對應 "Acme Software" 桶
var lost = map[c1]; // null 或 KeyNotFound

實值型別同樣危險

裝箱/拆箱會複製資料。加入集合後修改欄位,集合內的複製品不受影響,但邏輯上仍錯誤。

唯一解決之道:不可變型別

public class Customer
{
    private readonly string _name; // 唯讀!
    public string Name => _name;

    public Customer(string name) => _name = name;

    // 修改名稱 → 回傳新實例
    public Customer WithName(string newName) => new Customer(newName);

    public override int GetHashCode() => _name.GetHashCode();
}

正確使用模式


var c1 = new Customer("Acme Products");
map.Add(c1, orders);

var c2 = c1.WithName("Acme Software"); // 新實例,新雜湊值
var o = map[c1]; // 仍能找到原本
map.Remove(c1);
map.Add(c2, o);  // 以新鍵重新加入

雜湊函數實作指南

public override int GetHashCode()
{
    // 方案 1:單一不可變欄位
    return _keyField.GetHashCode();

    // 方案 2:多欄位 XOR(經典演算法)
    unchecked
    {
        int hash = 17;
        hash = hash * 23 + _field1.GetHashCode();
        hash = hash * 23 + _field2.GetHashCode();
        return hash;
    }

    // 方案 3:Tuple 組合 (C# 7+)
    return (_field1, _field2, _field3).GetHashCode();
}

三大規則檢查清單

  1. 相等物件雜湊碼相等:a.Equals(b) → a.GetHashCode() == b.GetHashCode()
  2. 雜湊碼不可變:參與雜湊的欄位必須不可變
  3. 分布均勻:減少衝突,提升查找效能

預設實作分析

  • Object.GetHashCode():物件 ID(正確但分布差,效能低)
  • ValueType.GetHashCode():第一個欄位雜湊碼(僅當第一欄位不可變且分布佳時正確)

核心建議

只有不可變型別才適合作雜湊鍵。若型別可變,不要覆寫 GetHashCode(),讓預設參考相等語意生效(或明確文件化不支援雜湊集合)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved