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

C# 深度解析:類型相等與參考恆等的關鍵差異

     編輯:關於C#

核心概念區分

概念定義關鍵特徵
Equality (值相等)兩個物件型別相同,且所有欄位值皆相等自反性、對稱性、傳遞性
Identity (參考恆等)兩個參考指向同一個記憶體位址記憶體位址相同

CLR 提供的四種比較機制

  1. Object.ReferenceEquals(a, b) — 純參考比較,忽略型別覆寫,值型別會裝箱 ⇒ 永遠回傳 false。
  2. Object.Equals(a, b) (static) — Null 安全的相等比較,內部呼叫實例 Equals。
  3. instance.Equals(other) (virtual) — 語意相等比較,可覆寫實作值比較邏輯。
  4. operator ==(a, b) — 運算子多載,編譯期靜態綁定,非多態。

值型別 vs 參考型別的預設行為

// 值型別 (struct)
struct Point { public int X, Y; }
var p1 = new Point { X = 1, Y = 2 };
var p2 = new Point { X = 1, Y = 2 };
Console.WriteLine(p1.Equals(p2));        // true  (ValueType.Equals 逐欄位比較)
Console.WriteLine(p1 == p2);             // 編譯錯誤,需自行多載 ==
Console.WriteLine(Object.ReferenceEquals(p1, p2)); // false (裝箱成不同物件)

// 參考型別 (class)
class Person { public string Name; }
var a = new Person { Name = "Tom" };
var b = a;
var c = new Person { Name = "Tom" };
Console.WriteLine(a == b);               // true  (同一參考)
Console.WriteLine(a == c);               // false (不同參考,預設比較參考)
Console.WriteLine(a.Equals(c));          // false (預設同參考比較)
Console.WriteLine(Object.ReferenceEquals(a, c)); // false

String 的特殊語意

string 是參考型別,但覆寫了 Equals 與 == 實作值語意:

string s1 = "hello";
string s2 = "he" + "llo";
Console.WriteLine(s1 == s2);        // true  (內容相等)
Console.WriteLine(Object.ReferenceEquals(s1, s2)); // true (字串暫存池最佳化)

正確覆寫 Equals 的標準模式

public class Employee : IEquatable<Employee>
{
    public int Id { get; }
    public string Name { get; }

    public Employee(int id, string name) => (Id, Name) = (id, name);

    // 1. 實作 IEquatable<T> 強型別比較
    public bool Equals(Employee other)
    {
        if (other is null) return false;
        return Id == other.Id && Name == other.Name;
    }

    // 2. 覆寫 Object.Equals
    public override bool Equals(object obj) => Equals(obj as Employee);

    // 3. 覆寫 GetHashCode (配合 Dictionary/HashSet 使用)
    public override int GetHashCode() => HashCode.Combine(Id, Name);

    // 4. 多載 == / != (建議)
    public static bool operator ==(Employee a, Employee b)
    {
        if (ReferenceEquals(a, b)) return true;
        if (a is null || b is null) return false;
        return a.Equals(b);
    }
    public static bool operator !=(Employee a, Employee b) => !(a == b);
}

Equals vs == 的多態差異陷阱


Animal a1 = new Dog("Lucky");
Animal a2 = new Dog("Lucky");

Console.WriteLine(a1.Equals(a2)); // true (虛擬呼叫 Dog.Equals)
Console.WriteLine(a1 == a2);      // false (編譯期綁定 Animal.operator==,預設參考比較)

結論:若型別實作值語意,務必同時覆寫 Equals、GetHashCode、多載 ==/!=,並實作 IEquatable<T> 避免裝箱。

實務建議速查表

  • 值型別:必須覆寫 Equals/GetHashCode/== 以提升效能(避免反射)。
  • 參考型別-值語意 (如 DTO、Entity、String):覆寫 Equals/GetHashCode/==,實作 IEquatable<T>。
  • 參考型別-參考語意 (大多數 Service、Controller、ViewModel):不要覆寫,使用預設參考比較。
  • 集合比較:使用 SequenceEqual 或自訂 `IEqualityComparer<T>`。

總結

釐清 Equality 與 Identity 的差異,掌握四種比較機制的適用場景,並遵循標準覆寫模式,是撰寫正確、高效、可維護 C# 程式碼的關鍵基石。

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