为什么覆盖了Equals方法后还要覆盖GetHashCode方法呢?
最近在读《Effective Java》,认为书中的每一条都十分经典。这回就简单记录一下Item 9。Item 9讲述的是“覆盖equals时总要覆盖hashCode”。我猜测,这条建议同时也会对C#适用。
请看下面这段代码:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.Collections;
6: namespace EqualsGetHashCode
7: {
8: class Program
9: {
10: static void Main(string[] args)
11: {
12: Dictionary<PhoneNumber, string> m = new Dictionary<PhoneNumber, string>();
13: m.Add(new PhoneNumber(707,867,5309), "Jenny");
14: Console.WriteLine(m[new PhoneNumber(707, 867, 5309)]==null);
15: Console.ReadKey();
16: }
17: }
18: public class PhoneNumber
19: {
20: public short AreaCode
21: {
22: get;
23: set;
24: }
25: public short Prefix
26: {
27: get;
28: set;
29: }
30: public short LineNumber
31: {
32: get;
33: set;
34: }
35: public PhoneNumber(int areaCode,int prefix,int lineNumber)
36: {
37: RangeCheck(areaCode, 999, "area code");
38: RangeCheck(prefix, 999, "prefix");
39: RangeCheck(lineNumber, 9999, "line number");
40: this.AreaCode = (short)areaCode;
41: this.Prefix = (short)prefix;
42: this.LineNumber = (short)lineNumber;
43: }
44: public static void RangeCheck(int arg, int max, string name)
45: {
46: if (arg < 0 || arg > max)
47: {
48: throw new ArgumentOutOfRangeException("arg", "arg must be less or equal than max");
49: }
50: }
51: public override bool Equals(object obj)
52: {
53: if (obj == this)
54: {
55: return true;
56: }
57: if (typeof(PhoneNumber) != obj.GetType())
58: {
59: return false;
60: }
61: PhoneNumber phoneNumber = obj as PhoneNumber;
62: return phoneNumber.LineNumber == LineNumber
63: && phoneNumber.AreaCode == AreaCode
64: && phoneNumber.Prefix == Prefix;
65: }
66: }
67: }
运行以上代码会抛出异常KeyNotFoundException。从字面来看,添加的PhoneNumber实例和欲检索的PhoneNumber实例是“等同”的,因为各个成员值都是相等的。但是运行时,却抛出异常。当我在用Java实验时,返回的会是null。也就是说未能在容器中找到与检索的实例,根据《Effective Java》便知是hashCode的问题了。既然实例相等,那么它们的哈希值也应该相等(来自Java的Object规范)。在Visual Studio中,发现当你进覆盖Equals方法而未实现GetHashCode方法时,会提示“EqualsGetHashCode.PhoneNumber' overrides Object.Equals(object o) but does not override Object.GetHashCode() ”。从中可以推出,在字典中检索时,是根据哈希值进行比较的。所以GetHashCode没有被覆盖,即使欲检索的实例在字典中存在,但是由于哈希函数没有被重写,最后结果还是不能如您预期。所以我们还要实现GetHashCode方法,而且还要求在语义上要和Equals方法一致。
添加一个GetHashCode方法:
1: public override int GetHashCode()
2: {
3: int result = 17;
4: result = 31 * result + AreaCode;
5: result = 31 * result + Prefix;
6: result = 31 * result + LineNumber;
7: return result;
8: }
现在再运行,发现控制台输出:False。
总结
当你覆盖Equals方法时,一定要覆盖GetHashCode方法,否则当你在使用一些容器类型时,会出现与您预期相违的结果。因为容器类是根据GetHashCode方法来和容易里的键做哈希值比较,所以要覆盖GetHashCode方法。其实这也符合常规,既然两个实例相等了,那它们的哈希值不相等也无道理的吧。所以请记住:当你覆盖Equals方法时,一定要覆盖GetHashCode方法。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?