型別 · 比較

C# 裡 == 與 .Equals()

在多數新手程式碼裡它們一致——而差異恰恰是 bug 藏身處:null 處理,以及每種型別對"相等"的定義。

對基元型別:行為完全一致

int x = 5;
int y = 5;
bool f = false;

Console.WriteLine(x == y);      // 值比較
Console.WriteLine(x.Equals(y)); // 對 int 來說同一個答案
Console.WriteLine(f.Equals(false));

對 int、double、bool、char 沒有實質區別——用 ==,讀起來更順。

對字串:結果相同,機制不同

string a = "apple";

Console.WriteLine(a == "apple");     // True  —— C# 讓 == 對字串比較內容
Console.WriteLine(a == "Apple");     // False —— 大小寫始終敏感
Console.WriteLine(a.Equals("apple"));   // True  —— 同樣是內容
Console.WriteLine(a.Equals("Apple"));   // False —— 同樣的大小寫規則

兩者都比字元內容,兩者都區分大小寫。(在其他語言裡 == 對字串比較引用——經典的 C#/Java 混淆;C# 把運算子過載成了"有用的那個"含義。)

差異一:null 處理

真正的行為分水嶺。== 對 null 從容;對 null 呼叫方法直接爆炸:

string s = null;

// == 是靜態檢查——任何一邊是 null 都沒事
Console.WriteLine(s == null);

// .Equals() 是對 s 的方法呼叫——s 是 null,所以先加護欄:
if (s != null)
{
    Console.WriteLine(s.Equals("x"));
}
else
{
    Console.WriteLine("safe");   // 走的是這裡——直接 s.Equals 會拋異常
}

實用規則:任何一邊可能為 null 時,用 ==(或 string.IsNullOrEmpty),別用 .Equals()。

差異二:"相等"的定義可以被重寫

Equals() 是一個類可以重定義的方法;== 是一個類可能過載的運算子。對你自己的類,兩者預設都不做內容比較——都是身份(struct vs class 一文裡 a == b → False 的演示):

class Dog { public string Name; }

Dog d1 = new Dog { Name = "Rex" };
Dog d2 = new Dog { Name = "Rex" };
Console.WriteLine(d1 == d2);        // False —— 不同物件
Console.WriteLine(d1.Equals(d2));   // False —— 同樣,除非重寫 Equals

record(C# 9+)和 struct 會把預設值改成按值比較——這正是資料型別紛紛改用 record 的原因之一。

日常贏家:忽略大小寫的比較

使用者輸入不會尊重你的大小寫。地道工具不是 == 也不是裸 Equals,而是帶比較型別的 Equals(Visual Studio 裡;瀏覽器引擎還沒有這個過載):

string input = "YES";

// 這三行是真實世界的模式:
input == "yes"                                        // False —— 大小寫敏感
input.Equals("yes", StringComparison.OrdinalIgnoreCase)   // True
input.ToLower() == "yes"                              // True —— 快糙版

優先 StringComparison.OrdinalIgnoreCase 而不是 ToLower() 鏈——它不分配記憶體,而且意圖擺在臉上。

決策表

比較什麼用
數字、bool、char==
字串,大小寫敏感==
字串,忽略大小寫Equals(…, OrdinalIgnoreCase)
任何可能為 null 的東西==(或 IsNullOrEmpty)
自己的內容相等型別做成 record,或重寫 Equals