C#中Dictionary使用实体类作为key时如何判断键是否存在
问题原因
C# 中默认的引用类型(class)的相等性比较逻辑是比较内存地址,你每次循环都new Psukim()生成新实例,哪怕所有属性值完全一致,两个实例的内存地址不同,Dictionary调用默认的Equals和GetHashCode判定时就会认为是完全不同的键,所以ContainsKey始终返回false。
解决方案
有三种常用实现方式,按需选择即可:
方案1:重写Psukim类的Equals和GetHashCode方法
直接修改Psukim类的定义,自定义相等性比较逻辑,示例代码如下:
public class Psukim { public int Book { get; set; } public int Pasuk { get; set; } public int Perek { get; set; } public override bool Equals(object obj) { if (obj is Psukim other) { return Book == other.Book && Pasuk == other.Pasuk && Perek == other.Perek; } return false; } public override int GetHashCode() { // 组合三个属性的哈希值,C# 7.0+ 支持该写法 return HashCode.Combine(Book, Pasuk, Perek); // 低版本C#可以用替换为下面的写法 // return Book.GetHashCode() ^ Pasuk.GetHashCode() ^ Perek.GetHashCode(); } }
方案2:将Psukim改为record类型(C# 9.0+ 推荐)
C# 9 引入的record类型默认实现了值语义的相等性比较,只要所有属性值相等就判定为两个实例相等,修改成本极低:
// 直接把class关键字改成record即可 public record Psukim { public int Book { get; set; } public int Pasuk { get; set; } public int Perek { get; set; } } // 也可以用更简洁的主构造函数写法 public record Psukim(int Book, int Pasuk, int Perek);
方案3:自定义相等比较器(无法修改Psukim类源码时使用)
如果不能改动Psukim类的代码,可以自定义IEqualityComparer<Psukim>,在初始化Dictionary时传入比较器即可:
// 自定义比较器 public class PsukimEqualityComparer : IEqualityComparer<Psukim> { public bool Equals(Psukim x, Psukim y) { if (x == null || y == null) return false; return x.Book == y.Book && x.Pasuk == y.Pasuk && x.Perek == y.Perek; } public int GetHashCode(Psukim obj) { return HashCode.Combine(obj.Book, obj.Pasuk, obj.Perek); } } // 初始化Dictionary时传入自定义比较器 Dictionary<Psukim, List<int>> showPsukim = new Dictionary<Psukim, List<int>>(new PsukimEqualityComparer());
额外优化建议
你现有代码里判断ContainsKey之后又取一次值,会执行两次字典查找,可以用TryGetValue简化逻辑,提升性能:
foreach (var item in allPsukim) { Psukim newPasuk = new Psukim() { Book = item.Book, Pasuk = item.Pasuk, Perek = item.Perek }; if (showPsukim.TryGetValue(newPasuk, out var posList)) { posList.Add(item.Position); } else { showPsukim.Add(newPasuk, new List<int> { item.Position }); } }
内容的提问来源于stack exchange,提问作者R-S
相关产品推荐
相关产品推荐

