重写GetHashCode方法后,参与计算的字段是否绝不能修改?
关于重写GetHashCode后字段修改的问题解答
核心结论
- 如果你的对象被用作
HashSet<T>元素、Dictionary<TKey, TValue>的键,或者任何依赖稳定哈希码的集合/组件,那参与GetHashCode计算的字段(你的代码里是b、c,还有疑似笔误的a.GetHashCode()应该是字段d)绝对不能修改。一旦修改,哈希码变化后,哈希集合会找不到这个对象,出现元素"丢失"、重复添加、查找失败等问题,这才是真正的"失效"。 - 如果对象只是用于普通的相等比较(比如
Equals判断),或者绑定场景(比如UI绑定,通常不依赖哈希码的稳定性),修改这些字段不会导致GetHashCode"失效"——只是哈希码会跟着字段值变化而已,这时候只要Equals方法也是基于这些字段实现的,两者逻辑一致,不会有问题。
为什么日常很多可变对象也会重写GetHashCode
你担心"99%的场景下重写GetHashCode都不安全"其实是多虑了,因为大部分场景里:
- 对象不会被放进哈希集合当键/元素,只是用来做值比较,这时候哈希码变就变了,不影响使用。
- 就算会放进哈希集合,只要能保证放入集合后不再修改相关字段,也不会有问题。
怎么避免踩坑
- 优先做不可变对象:把参与哈希计算的字段设为
readonly,从根源上杜绝修改的可能,这样对象的哈希码永远稳定,最安全。 - 明确使用规则:如果对象必须可变,就要约定好——在把对象放入哈希集合前可以修改,一旦放进去,就不能再改那些影响哈希码的字段;如果非要改,先把对象从集合里移除,修改完再重新添加。
- 修正代码笔误:你代码里的
a.GetHashCode()明显有问题,类名是a,这里应该是实例字段d.GetHashCode()吧?不然你是在拿类的类型哈希码来计算,不是实例的字段值,这会导致所有a实例的哈希码都包含同一个固定值,违背了哈希码的设计初衷。
内容的提问来源于stack exchange,提问作者rick
相关产品推荐
相关产品推荐

