以自定义类为键时如何正确使用TryGetValue方法?关于Dictionary中TryGetValue参数的疑问
你的代码示例
var ambassadors = new Dictionary<CountryCode, Ambassador>(); Ambassador england = new Ambassador { CountryCode = new CountryCode("eng"), Name = "John", Age = 25 }; Ambassador australia = new Ambassador { CountryCode = new CountryCode("aus"), Name = "Martin", Age = 49 }; ambassadors.Add(england.CountryCode, england); ambassadors.Add(australia.CountryCode, australia); Console.WriteLine("Enter country code: "); var code = Console.ReadLine(); if (ambassadors.TryGetValue(new CountryCode(code), out Ambassador ambassador)) { Console.WriteLine($"The ambassador is {ambassador.Name}"); } else { Console.WriteLine("The ambassador with the given code does not exist in the dictionary"); } Console.ReadLine(); } public class Ambassador { public CountryCode CountryCode { get; set; } public string Name { get; set; } public int Age { get; set; } } public class CountryCode { public string Code { get; } public CountryCode(string code) { Code = code; } public override bool Equals(object obj) { if (obj == null) { return false; } if (!(obj is CountryCode)) { return false; } return StringComparer.OrdinalIgnoreCase.Equals(this.Code, ((CountryCode)obj).Code); } public override int GetHashCode() { return StringComparer.OrdinalIgnoreCase.GetHashCode(this.Code); }
你的核心疑问
为什么调用ambassadors.TryGetValue时,必须新建一个CountryCode实例作为参数?明明字典里已经存在对应的CountryCode实例,为什么不能直接检查这些已存在的实例?
问题解析
其实这个问题的核心是要理解Dictionary的查找逻辑,以及自定义键类型的工作方式,我分几点给你解释:
Dictionary靠「键的相等性」查找,而非实例引用
Dictionary的本质是哈希表,它判断两个键是否相等,不是看两个实例是不是同一个内存引用,而是通过你重写的Equals和GetHashCode方法来判断。只要两个CountryCode实例的Code(忽略大小写)相同,它们就会被Dictionary视为同一个键——这也是你正确重写这两个方法的意义所在。你没有现成的匹配实例可以直接传入
从控制台输入的是字符串code,你手里只有这个字符串,并没有一个对应的CountryCode实例可以直接用。字典里的CountryCode实例是之前创建的,但你输入的时候并不知道用户会输入什么,也没法提前拿到对应的实例(总不能遍历字典先找一遍吧?那Dictionary的O(1)高效查找优势就没了)。TryGetValue的参数类型要求必须是TKey
你的字典键类型是CountryCode,所以TryGetValue的第一个参数必须是CountryCode类型的对象,不能直接传字符串。所以你必须把输入的字符串转换成CountryCode实例,这里最简单的方式就是新建一个——而由于你已经正确重写了相等性判断方法,这个新建的实例和字典里已有的对应实例会被判定为相等,自然就能找到对应的值。
举个额外的例子:如果提前把所有CountryCode实例存在一个列表里,你也可以先遍历列表找到和输入字符串匹配的实例,再把那个实例传给TryGetValue,但这样做的效率是O(n),远不如直接新建实例做O(1)的哈希查找高效。
内容的提问来源于stack exchange,提问作者Vincent Mazzarella

