结论:不重写gethashcode就重写equals会引发哈希容器查找失败等隐蔽问题;必须确保equals与gethashcode逻辑一致、基于不可变字段,且配对实现iequatable以避免性能损耗和语义错误。

直接说结论:不重写 GetHashCode 就重写 Equals,等于埋雷——Dictionary、HashSet 一用就错,而且问题往往延迟暴露,查起来特别费劲。
为什么 Dictionary 查不到刚加进去的对象?
这是最典型的翻车现场:对象加进 Dictionary<person string></person> 后,改了它的 Name 字段,再用原实例去 TryGetValue 就返回 false。
原因不是“查法不对”,而是哈希码变了,桶位置失效了。因为:
-
Dictionary内部靠GetHashCode()快速定位桶,再用Equals()做精确比对 - 如果
GetHashCode返回值依赖可变字段(比如Name),而你又没在修改后重新哈希——它就永远找不到自己 - 更隐蔽的是:即使你没改字段,只要
GetHashCode和Equals逻辑不一致(比如Equals比了ID和Name,GetHashCode却只基于ID),也会导致漏匹配
GetHashCode 必须满足的硬约束
它不是“生成唯一 ID”,而是为哈希容器服务的分桶依据。三条铁律必须同时满足:
- 只要
Equals(a, b) == true,那么a.GetHashCode() == b.GetHashCode()必须成立 - 对象在生命周期内只要参与哈希容器操作,其
GetHashCode返回值就不能变(即:只能基于不可变字段,或确保字段不变) - 不同对象可以返回相同哈希码(哈希冲突允许),但尽量分散能提升性能
常见错误写法:return Name.GetHashCode() ^ Age.GetHashCode(); —— 如果 Name 可能为 null,会抛 NullReferenceException;更糟的是,如果后续允许改 Name,就违反第二条。
Equals 和 GetHashCode 的配对写法(含 null 安全)
标准模板不是为了炫技,是堵住所有隐性崩塌点:
public override bool Equals(object obj)
{
var other = obj as Person;
if (other is null) return false;
return ID == other.ID && string.Equals(Name, other.Name, StringComparison.Ordinal);
}
public override int GetHashCode()
{
// ID 是不可变字段(如数据库主键、构造时设死),Name 也假设不可变
// 若 Name 可变,则此处不能参与哈希计算
return HashCode.Combine(ID, Name);
}
注意点:
- 用
as+is null替代is Person other,避免子类绕过比较逻辑 - 字符串比较用
string.Equals(a, b, StringComparison.Ordinal),不依赖==且防空 -
HashCode.Combine是 .NET Core 2.1+ 推荐方式,比手写^更安全、更均匀 - 如果类型是
record,编译器自动生成两者,但前提是所有参与比较的属性本身也满足值语义(比如没包含List<t></t>这种引用类型字段)
IEquatable 不是可选项,是性能刚需
仅重写 Object.Equals(object) 会导致装箱(值类型)或虚调用开销(引用类型)。真实项目里该接口几乎必上:
- 实现
IEquatable<person></person>接口,提供bool Equals(Person other)方法 - 让
Object.Equals(object)内部直接调用这个强类型版本,避免类型检查和转换 - 集合如
List<t>.Contains</t>、Dictionary<k></k>都会优先使用IEquatable<t></t>,性能提升明显
容易忽略的一点:如果你的类有继承关系,且父类已实现 IEquatable<base>,子类要实现 IEquatable<derived></derived> 并在 Equals(Derived) 中调用 base.Equals(other as Base),否则可能漏比基类字段。
最麻烦的地方不在写法本身,而在于“什么时候该认为两个对象相等”——业务语义一旦模糊,Equals 和 GetHashCode 就会变成定时炸弹。比如一个 Person 对象,ID 相同但姓名不同,算不算相等?这个问题不先拍板,代码写得再规范也没用。











