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

C# List.Remove 失效陷阱:引用相等 vs 值相等,正確實現 IEquatable 避免「不死對象」

     編輯:關於C#

嘗試從集合移除對象後,對象仍留在集合中?這是因為 List<T>.Remove 使用預設相等比較器 (EqualityComparer<T>.Default),對引用類型比較的是引用地址而非內容。

問題重現

IList<Product> products = new List<Product>();
products.Add(GetProduct("1412"));      // 實例 A
products.Remove(GetProduct("1412"));   // 實例 B (新實例)
Console.WriteLine(products.Count);     // 輸出 1 (未移除!)

GetProduct 每次從 DB 讀取返回新實例。Product 未重寫 Equals/GetHashCode,繼承自 Object,故比較引用。

原理深挖

  • List<T>.Remove → EqualityComparer<T>.Default。
  • 若 T 實作 IEquatable<T>,使用其 Equals(T)。
  • 否則回退 Object.Equals (引用相等) + Object.GetHashCode。

因此,兩個內容相同但引用不同的 Product 實例被視為不相等,Remove 失效。

兩種修復方案

方案一:實作 IEquatable<T> (推薦,強類型、高性能)

public class Product : IEquatable<Product> {
    public string ID { get; }
    public Product(string id) { ID = id; }

    public bool Equals(Product other) {
        return other != null && ID == other.ID;
    }
    public override bool Equals(object obj) => Equals(obj as Product);
    public override int GetHashCode() => ID.GetHashCode();
}

方案二:重寫 Object.Equals/GetHashCode

public class Product {
    public string ID { get; }
    public Product(string id) { ID = id; }

    public override bool Equals(object obj) {
        return obj is Product other && ID == other.ID;
    }
    public override int GetHashCode() => ID.GetHashCode();
}

最佳實踐建議

  1. 值語義類型 (實體、DTO、VO) 必須實作值相等邏輯 (IEquatable<T> + 重寫 Equals/GetHashCode + 運算符 ==/!=)。
  2. 業務鍵 (如 ID) 作為相等依據,而非所有字段。
  3. 避免在集合操作中重新查詢數據庫獲取新實例;應保持實例引用或實現正確相等性。
  4. 「同一顆子彈不打兩次」:邏輯相等判斷要正確,避免集合操作、字典鍵、去重等場景出現「不死對象」。

修復後,products.Remove(new Product("1412")) 即可正常移除內容相同的實例。

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