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

C# 相等性比較深度解析:Equality vs Identity、四種比較方式與最佳實務

     編輯:關於C語言

核心概念區分

概念定義記憶體語意必要條件
Equality (相等)型別相同且所有欄位值相等不同記憶體位址也可相等Reflexive (a==a)、Symmetric (a==b ⇒ b==a)、Transitive (a==b ∧ b==c ⇒ a==c)
Identity (恆等/同一性)參考同一個物件實例共享同一記憶體位址ReferenceEquals 為 true

四種比較機制


// 1. 靜態 ReferenceEquals - 永遠比較 Identity
public static bool ReferenceEquals(object left, object right);

// 2. 靜態 Object.Equals - 比較 Equality (內部呼叫實例 Equals)
public static bool Equals(object left, object right);

// 3. 實例虛擬 Equals - 型別自訂 Equality 邏輯
public virtual bool Equals(object right);

// 4. 運算子 == - 可多載,行為由型別定義
public static bool operator ==(MyType left, MyType right);

預設行為差異表

型別類別ReferenceEqualsObject.Equals (靜態/實例)運算子 ==
參考型別 (class)比較參考比較參考 (同 ReferenceEquals)比較參考 (未多載時)
實值型別 (struct)**永遠 false** (裝箱產生不同物件)**逐欄位值比較** (透過反射,較慢)**不支援** (未多載無法編譯)
String (特殊參考型別)比較參考**比較字元內容** (已多載)**比較字元內容** (已多載)

正確實作 Equals / == / GetHashCode (實值型別範例)


public readonly struct Point : IEquatable
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y) { X = x; Y = y; }

    // 1. 強型別 Equals (避免裝箱)
    public bool Equals(Point other) => X == other.X && Y == other.Y;

    // 2. 覆寫 Object.Equals
    public override bool Equals(object obj) => obj is Point p && Equals(p);

    // 3. 覆寫 GetHashCode (哈希碼一致性:Equals 為 true ⇒ HashCode 相同)
    public override int GetHashCode() => HashCode.Combine(X, Y); // .NET Core 2.1+

    // 4. 多載 == / !=
    public static bool operator ==(Point left, Point right) => left.Equals(right);
    public static bool operator !=(Point left, Point right) => !left.Equals(right);
}

參考型別實作指南 (Entity 類別)


public class User : IEquatable
{
    public Guid Id { get; init; }
    public string Email { get; init; }

    // 業務鍵相等 (如同一 Email 視為同一用戶)
    public bool Equals(User other)
    {
        if (other is null) return false;
        if (ReferenceEquals(this, other)) return true;
        return Email == other.Email; // 或 Id 比較
    }

    public override bool Equals(object obj) => Equals(obj as User);

    public override int GetHashCode() => Email?.GetHashCode() ?? 0; // 不可變欄位

    public static bool operator ==(User left, User right)
    {
        if (left is null) return right is null;
        return left.Equals(right);
    }
    public static bool operator !=(User left, User right) => !(left == right);
}

多態陷阱:== vs Equals


object a = new Point(1,2);
object b = new Point(1,2);

Console.WriteLine(a == b);        // false (編譯期型別 object,呼叫 object.== → 參考比較)
Console.WriteLine(a.Equals(b));   // true  (虛擬呼叫 Point.Equals)
Console.WriteLine((Point)a == (Point)b); // true (編譯期型別 Point,呼叫 Point.==)

**結論**:泛型程式碼/物件型別變數**永遠用 `Equals`**,避免 `==` 多態失效。

ValueTuple / Record 現代簡化 (.NET 5+ / C# 9+)


// Record 自動產生 Equals/GetHashCode/==/!=/ToString/with-expression
public record Person(string Name, int Age, Address Address);

// ValueTuple 結構化相等
var t1 = (Name: "Tom", Age: 30);
var t2 = (Name: "Tom", Age: 30);
Console.WriteLine(t1 == t2); // true

最佳實務總結

  1. 實值型別:務必實作 `IEquatable`、`Equals`、`GetHashCode`、`==`/`!=`,避免反射效能損耗。
  2. 參考型別 (Entity):依業務鍵實作 `Equals`/`GetHashCode`,多載 `==`/`!=` 代理 `Equals`。
  3. 不可變型別:`GetHashCode` 使用不可變欄位,確保字典/集合正確運作。
  4. 泛型/物件變數:永遠用 `Equals`,不用 `==`。
  5. 集合/字典鍵:正確實作 `GetHashCode`,否則查找失效。
  6. Record/ValueTuple:優先使用現代語法自動生成正確相等邏輯。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved