类型 · 比较
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 |