值类型必须实现 iequatable,否则集合查找等操作会因装箱导致性能差、类型不安全且易出逻辑错误;需同步重写 equals(object) 和 gethashcode(),确保行为一致、哈希稳定、空值安全。

直接说结论:不实现 IEquatable<t></t>,集合查找、字典键比较、LINQ 的 Contains/Distinct 等操作会默默退化为装箱 + Object.Equals,性能差、类型不安全、还容易出逻辑错。
为什么值类型必须实现 IEquatable
结构体(struct)调用 Object.Equals(object) 时必然触发装箱 —— 即使你只比一个 int 字段,每次比较都要分配堆内存。而 IEquatable<t>.Equals(T)</t> 是泛型方法,编译器生成的是专用代码,零装箱。
-
public struct Point : IEquatable<point></point>是正确起点;只重写Equals(object)是半残废 - 如果不实现
IEquatable<point></point>,new List<point>().Contains(p)</point>内部会走Object.Equals,装箱不可免 - VS 的代码分析规则
CA1067就是专抓这种“实现了接口却不覆写Equals(object)”的漏网之鱼
class 实现 IEquatable 时的 null 处理陷阱
IEquatable<t>.Equals(T other)</t> 的参数允许为 null(对引用类型),但它的契约不保证 other 非空 —— 所以你得自己判空,且必须和 Equals(object) 的行为严格一致。
- 错误写法:
public bool Equals(Person other) => other.LastName == this.LastName;——other为null时直接 NRE - 正确写法:
public bool Equals(Person? other) => other is not null && other.NationalId == this.NationalId; -
override Equals(object obj)必须转成同类型再调用上面那个方法,不能自己再写一遍逻辑,否则两套判断不一致
Equals 和 GetHashCode 必须成对修改
只要改了相等逻辑,GetHashCode() 就必须同步更新 —— 否则放进 Dictionary<tkey tvalue></tkey> 或 HashSet<t></t> 里会找不到对象。
- 如果用
NationalId判等,GetHashCode()就得基于NationalId.GetHashCode(),不能用LastName或时间戳 - 多个字段参与比较时,推荐用
HashCode.Combine(field1, field2)(.NET Core 2.1+),别手写x * 31 + y - 若字段可能为
null(如string),GetHashCode()要用?.GetHashCode() ?? 0避免 NRE
比 record 更轻量,但更易出错
record 自动实现值相等性,省心;但如果你需要细粒度控制(比如只按 ID 比、忽略时间戳或临时状态字段),就得手动实现 IEquatable<t></t> —— 这时候最容易忽略的是对称性与可传递性。
- 别在
Equals里调用外部服务、读文件、或依赖当前时间 —— 违反「相同输入必得相同输出」原则 - 两个对象互相
Equals返回true,但其中一个被修改后,另一个再调用Equals仍应返回true(只要没改参与比较的字段) - 最常被忽略的一点:
GetHashCode()在对象生命周期内必须稳定 —— 即使你后来改了非参与比较的字段,哈希码也不能变











