C# 中 Equals 与 == 的区别
在 C# 中,Equals 和 == 都可以用来“比较”,但它们比较的逻辑并不完全相同。最核心的一句话是:
==是运算符,可以被重载;Equals()是方法,通常用于判断对象的“相等性”。
可以从几个层面理解。
1. 基本类型:看起来基本一样
int a = 10;
int b = 10;
Console.WriteLine(a == b); // True
Console.WriteLine(a.Equals(b)); // True
对于 int、double、bool 等值类型,通常都是比较值。
例如:
double a = 3.14;
double b = 3.14;
a == b; // True
a.Equals(b); // True
2. 引用类型:== 默认比较引用,Equals() 可以比较内容
这是最重要的区别。
class Person
{
public string Name { get; set; }
}
Person p1 = new Person { Name = "张三" };
Person p2 = new Person { Name = "张三" };
Console.WriteLine(p1 == p2);
Console.WriteLine(p1.Equals(p2));
如果 Person 没有重写任何方法:
p1 == p2 → False
p1.Equals(p2) → False
原因是:
p1 ──→ 对象A { Name = "张三" }
p2 ──→ 对象B { Name = "张三" }
虽然两个对象的内容一样,但它们是两个不同的对象。
默认情况下:
p1 == p2
比较的是两个引用是否指向同一个对象。
而:
p1.Equals(p2)
如果 Person 没有重写 Equals(),最终也是按照引用来判断。
3. string 是一个非常典型的例外
string s1 = "hello";
string s2 = "hello";
Console.WriteLine(s1 == s2); // True
Console.WriteLine(s1.Equals(s2)); // True
很多初学者会认为:
“引用类型使用
==比较地址。”
这句话并不完全正确。
因为 == 可以被重载。
string 就重载了 ==,所以:
s1 == s2
比较的是字符串内容。
也就是说:
string a = "ABC";
string b = "ABC";
a == b
实际上可以理解为:
比较字符串内容是否相同
而不是简单比较两个引用。
4. 自定义类可以重写 Equals
例如:
class Person
{
public string Name { get; set; }
public override bool Equals(object obj)
{
Person other = obj as Person;
if (other == null)
return false;
return Name == other.Name;
}
public override int GetHashCode()
{
return Name?.GetHashCode() ?? 0;
}
}
现在:
Person p1 = new Person { Name = "张三" };
Person p2 = new Person { Name = "张三" };
Console.WriteLine(p1 == p2);
Console.WriteLine(p1.Equals(p2));
结果仍然可能是:
False
True
因为你只重写了 Equals(),没有重载 ==。
这说明:
== → 看 == 运算符的实现
Equals → 看 Equals() 的实现
两者是相互独立的。
5. 如果同时重载 == 和 Equals
可以这样:
class Person
{
public string Name { get; set; }
public override bool Equals(object obj)
{
Person other = obj as Person;
return other != null && Name == other.Name;
}
public override int GetHashCode()
{
return Name?.GetHashCode() ?? 0;
}
public static bool operator ==(Person a, Person b)
{
if (ReferenceEquals(a, b))
return true;
if (a is null || b is null)
return false;
return a.Name == b.Name;
}
public static bool operator !=(Person a, Person b)
{
return !(a == b);
}
}
那么:
Person p1 = new Person { Name = "张三" };
Person p2 = new Person { Name = "张三" };
Console.WriteLine(p1 == p2); // True
Console.WriteLine(p1.Equals(p2)); // True
这时候两种比较的语义就统一了。
6. ReferenceEquals() 又是什么?
C# 中还有一个非常重要的方法:
object.ReferenceEquals(a, b)
它的含义非常明确:
判断两个变量是否指向同一个对象。
例如:
Person p1 = new Person();
Person p2 = new Person();
Person p3 = p1;
Console.WriteLine(p1 == p2); // False
Console.WriteLine(object.ReferenceEquals(p1,p2)); // False
Console.WriteLine(p1 == p3); // True
Console.WriteLine(object.ReferenceEquals(p1,p3)); // True
可以把三个概念简单理解成:
==
↓
运算符比较
可以被重载
具体比较什么取决于类型
Equals()
↓
相等性比较
可以被重写
通常用于判断两个对象“逻辑上是否相等”
ReferenceEquals()
↓
引用比较
判断是不是同一个对象
7. 一个非常重要的实际例子
假设:
class User
{
public int Id { get; set; }
public string Name { get; set; }
}
现在:
User u1 = new User
{
Id = 1,
Name = "张三"
};
User u2 = new User
{
Id = 1,
Name = "张三"
};
虽然:
u1.Id == u2.Id
u1.Name == u2.Name
但是:
u1 == u2
默认仍然是:
False
因为:
u1 → 对象A
u2 → 对象B
如果你的业务定义是:
Id 相同就认为是同一个用户
那么就应该重写 Equals(),甚至同时重载 ==。
8. 实际开发中怎么选择?
可以记这个表:
| 场景 | 推荐 |
|---|---|
int、double、bool 等值类型比较 |
== |
| 字符串比较 | == 或 Equals() |
| 判断两个对象逻辑上是否相等 | Equals() |
| 判断是不是同一个对象 | ReferenceEquals() |
自定义类型需要 == 表示业务相等 |
重载 == |
| Dictionary / HashSet 判断对象相等 | Equals() + GetHashCode() |
尤其需要注意:
Equals()
如果你重写了,就应该同时考虑:
GetHashCode()
因为:
Dictionary<TKey, TValue>
HashSet<T>
等集合会依赖对象的相等性和哈希值。
9. 最容易记错的地方
不要简单记成:
== = 比较地址
Equals = 比较内容
这是不准确的。
正确理解应该是:
==
→ 运算符
→ 可以重载
→ 不同类型的行为可能不同
Equals()
→ 方法
→ 可以重写
→ 用来定义“两个对象是否相等”
ReferenceEquals()
→ 专门判断两个引用是否指向同一个对象
例如:
string a = new string(new[] { 'a', 'b', 'c' });
string b = new string(new[] { 'a', 'b', 'c' });
Console.WriteLine(a == b); // True
Console.WriteLine(a.Equals(b)); // True
Console.WriteLine(object.ReferenceEquals(a, b)); // False
这个例子非常适合理解三者的区别:
a 和 b
内容相同 → == True
逻辑相等 → Equals True
是不是同一个对象 → ReferenceEquals False

浙公网安备 33010602011771号