在C#中,判断两个对象是否“逻辑相等”,不能只靠
==或默认的
Equals()——尤其是自定义类。关键在于正确重写
Equals(object)和
GetHashCode(),二者必须成对出现、逻辑一致。否则可能在字典、哈希集里出错,或让相等判断结果不符合预期。
为什么必须同时重写 Equals 和 GetHashCode
这两个方法是 .NET 哈希容器(如
Dictionary<tkey tvalue></tkey>、
HashSet<t></t>)正常工作的基础:
GetHashCode()用于快速分桶:如果两个对象相等,它们的哈希码 必须相同
Equals()用于精确比对:哈希码相同只是“可能相等”,最终靠
Equals()确认 反之,如果
GetHashCode()不一致,
Equals()根本不会被调用(直接判为不等) 若只重写
Equals()而忽略
GetHashCode(),对象放进
Dictionary后可能查不到自己
重写 Equals 的基本步骤
以一个简单
Person类为例,按规范重写
Equals(object obj): 先检查
obj是否为
null或类型不匹配(用
obj is Person other最简洁) 比较所有参与逻辑相等判断的字段(通常是
public或
private的值型/引用型字段) 对引用类型字段,用
Equals(a, b)(静态方法,可安全处理
null) 对值类型字段,直接用
==即可 如果类有继承关系,需调用基类
base.Equals(obj)示例:
public override bool Equals(object obj) {
if (obj is Person other) {
return Id == other.Id &&
string.Equals(Name, other.Name, StringComparison.Ordinal) &&
BirthDate == other.BirthDate;
}
return false;
}重写 GetHashCode 的要点
哈希码不要求唯一,但要求:相等的对象必须返回相同哈希码;尽量让不等对象哈希码分布均匀(减少哈希冲突):
只基于Equals中用到的那些字段计算哈希 避免使用可变字段(如后续会修改的属性),否则对象加入哈希集合后修改字段,哈希码变化 → 查找失败 C# 9+ 推荐用元组解构简化写法:
return (Id, Name, BirthDate).GetHashCode();老版本可用
HashCode.Combine(Id, Name, BirthDate)(.NET Core 2.1+)或手动组合(如
Id.GetHashCode() ^ (Name?.GetHashCode() ?? 0) )示例:
public override int GetHashCode() {
return (Id, Name, BirthDate).GetHashCode();
}额外建议和常见坑
考虑实现IEquatable<t></t>接口,提供类型安全的
Equals(T other),避免装箱,性能更好 重载
==和
!=运算符时,内部应调用
Equals(),保持语义一致 记录类型(
record)自动实现结构相等和哈希,适合不可变数据场景 如果类是可变的,且字段可能被修改,务必确保修改不涉及参与
Equals判断的字段,否则哈希容器行为不可预测
基本上就这些。核心就一条:Equals 怎么比,GetHashCode 就怎么算——不多不少,不变不乱。
