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

C# 核心面試題:Equals、ReferenceEquals 與 == 三者差異完整解析

     編輯:關於.NET

「Equals 和 == 有什麼區別?」是 C# 面試經典題,但許多回答停留在「比較值 vs 比較參考」的過度簡化。本文從型別系統根源、靜態/虛擬方法、運算子多載、實際測試代碼四個維度,建立精確、完整的認知框架。

測試類別定義


// 參考型別
public class ReferenceClass
{
    public int X { get; set; }
    public string Y { get; set; }
}

// 值型別
public struct ValueStruct
{
    public int X;
    public string Y;
}

參考型別比較行為驗證


var c1 = new ReferenceClass { X = 1, Y = "OK" };
var c2 = new ReferenceClass { X = 1, Y = "OK" };  // 內容相同,不同實例
var c3 = new ReferenceClass { X = 1, Y = "OK!" }; // 內容不同
var c4 = new ReferenceClass { X = 2, Y = "OK" };  // 內容不同
var c5 = c1;  // 同一參考
var c6 = c1;  // 同一參考
c5.X = 2;     // 修改 c5 影響 c1

// 測試四種比較方式
Console.WriteLine("=== ReferenceEquals (靜態) ===");
Console.WriteLine(ReferenceEquals(c1, c2)); // False
Console.WriteLine(ReferenceEquals(c1, c3)); // False
Console.WriteLine(ReferenceEquals(c1, c4)); // False
Console.WriteLine(ReferenceEquals(c1, c5)); // True
Console.WriteLine(ReferenceEquals(c1, c6)); // True

Console.WriteLine("=== Equals 靜態方法 ===");
Console.WriteLine(Equals(c1, c2)); // False
Console.WriteLine(Equals(c1, c3)); // False
Console.WriteLine(Equals(c1, c4)); // False
Console.WriteLine(Equals(c1, c5)); // True
Console.WriteLine(Equals(c1, c6)); // True

Console.WriteLine("=== 實例虛擬方法 c1.Equals() ===");
Console.WriteLine(c1.Equals(c2)); // False
Console.WriteLine(c1.Equals(c3)); // False
Console.WriteLine(c1.Equals(c4)); // False
Console.WriteLine(c1.Equals(c5)); // True
Console.WriteLine(c1.Equals(c6)); // True

Console.WriteLine("=== == 運算子 ===");
Console.WriteLine(c1 == c2); // False
Console.WriteLine(c1 == c3); // False
Console.WriteLine(c1 == c4); // False
Console.WriteLine(c1 == c5); // True
Console.WriteLine(c1 == c6); // True

參考型別結論

四種方式全部比較參考位址 (預設行為)。ReferenceEquals、Equals 靜態、Equals 實例、== 結果完全一致。

值型別比較行為驗證


var v1 = new ValueStruct { X = 1, Y = "A" };
var v2 = new ValueStruct { X = 1, Y = "A" }; // 內容相同
var v3 = new ValueStruct { X = 2, Y = "A" };
var v4 = v1; // 複本 (值型別賦值即複製)

Console.WriteLine("=== ReferenceEquals (值型別) ===");
Console.WriteLine(ReferenceEquals(v1, v2)); // False (裝箱產生不同物件)
Console.WriteLine(ReferenceEquals(v1, v1)); // False (每次裝箱新物件)

Console.WriteLine("=== Equals 靜態/實例 ===");
Console.WriteLine(Equals(v1, v2)); // True (內容相等)
Console.WriteLine(v1.Equals(v2));  // True

Console.WriteLine("=== == 運算子 ===");
// v1 == v2 編譯錯誤:值型別預設不支援 ==,除非自行多載

值型別結論

  • ReferenceEquals:永遠 false (除非 null),因裝箱產生不同堆上物件
  • Equals 靜態/實例:比較內容值 (逐欄位遞迴比較)
  • == 運算子:預設不可用,需自行多載

四大比較機制深度解析

機制分類參考型別預設行為值型別預設行為可自訂Null 安全
ReferenceEquals靜態方法參考位址比較永遠 false (裝箱)❌✅ (null==null 為 true)
Equals(object) 靜態靜態方法參考位址比較內容值比較間接 (透過實例虛擬方法)✅ (內部檢查 null)
Equals(object) 實例虛擬虛擬方法參考位址比較內容值比較 (ValueType 覆寫)✅ (覆寫)❌ (this 為 null 拋例外)
== 運算子靜態運算子參考位址比較不可用 (需多載)✅ (多載)視實作而定

關鍵差異總結

1. ReferenceEquals:唯一純粹的參考比較

  • 不可覆寫、不可多載
  • 值型別必須裝箱 → 永遠不同實例 → 永遠 false
  • 適合:確認是否為同一實例、null 檢查

2. Equals 靜態方法:Null 安全的通用比較


public static bool Equals(object objA, object objB)
{
    if (objA == objB) return true;  // 同參考或同為 null
    if (objA == null || objB == null) return false;
    return objA.Equals(objB);  // 委託給實例方法
}
  • 優先處理 null,不拋例外
  • 最終調用實例虛擬 Equals
  • 適合:不確定是否為 null 時的安全比較

3. 實例虛擬 Equals:可自訂的語意相等

  • Object.Equals:參考比較
  • ValueType.Equals:反射逐欄位比較 (效能差,建議覆寫)
  • String.Equals:內容比較 (已優化)
  • 自訂類別**務必覆寫** 配合 GetHashCode

4. == 運算子:語法糖,行為由類別定義

  • 內建型別 (int, string 等):值比較 (含隱含轉型)
  • 參考型別:預設參考比較 (同 ReferenceEquals)
  • String 多載 == 為內容比較 (最常見陷阱)
  • 值型別:必須自行 public static bool operator ==(T a, T b)

最佳實踐檢查清單

  1. 自訂類別:覆寫 Equals(object)、GetHashCode()、多載 ==/!=、實作 IEquatable<T>
  2. 值型別 (struct):務必覆寫 Equals 避免反射效能損失,多載 ==/!=
  3. 參考比較需求:明確使用 ReferenceEquals 或 object.ReferenceEquals
  4. Null 安全比較:使用 Equals(objA, objB) 靜態方法
  5. 字串比較:== 和 Equals 皆為內容比較,但 Equals 可指定 StringComparison (序數/文化特性/忽略大小寫)

精準掌握三者差異,不僅能通過面試,更能避免生產環境中因比較邏輯錯誤導致的詭異 Bug。

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