型別 · 比較
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 |