C#自定义类作为Dictionary键单字段匹配失效问题
问题根因
- 你观察到的「单字段匹配时不进入Equals方法」是Dictionary的原生查找机制导致的:Dictionary查找键时第一步先计算待查找键的哈希码,只会和哈希桶中存储的、哈希码完全一致的条目调用Equals做精确匹配,哈希码不一致的条目会被直接跳过,根本不会触发Equals调用。
- 你当前的
GetHashCode实现是_s1.GetHashCode() ^ _s2.GetHashCode(),结合测试用例的几个对象就能解释现象:- 已存入的
key:s1="1"、s2="2",哈希值为"1"的哈希 ^ "2"的哈希 - 已存入的
key2:s1="12"、s2="21",哈希值为"12"的哈希 ^ "21"的哈希 - 待查找的
lookUp2:s1="1"、s2="32",哈希值为"1"的哈希 ^ "32"的哈希
三个哈希值完全不重合,所以查找lookUp2时没有任何同哈希的候选条目,自然不会进入Equals方法。而完全匹配的lookUp1因为s1、s2和key完全一致,异或得到的哈希值和key相同,所以会触发Equals调用,和你观察到的日志现象完全吻合。
- 已存入的
- 比哈希实现错误更严重的问题是:你定义的「s1相等 或 s2相等就算键匹配」的规则,完全违反了IEqualityComparer的强制契约,根本无法在Dictionary中正常工作。IEqualityComparer要求相等判定必须满足传递性:如果A等于B,B等于C,那么A必须等于C。你的OR规则显然不满足这个要求:
举个反例:A(s1="a",s2="1")、B(s1="a",s2="2")、C(s1="b",s2="2")
按你的规则A等于B(s1相同),B等于C(s2相同),但A和C没有任何字段相同,判定为不相等,完全破坏了等价关系。
Dictionary的存储、查找、去重逻辑完全依赖这个相等契约,违反契约会导致重复键、查找漏匹配、内存泄漏等各种不可预期问题,哪怕你把GetHashCode改成固定返回同一个值(强制所有条目都走Equals),也解决不了逻辑矛盾:一个查找键同时匹配多个已存键时,Dictionary根本不知道该返回哪个对应值。
可行解决方案
你的业务匹配规则本质上不支持Dictionary的O(1)哈希查找逻辑,不要尝试通过修改EqualityComparer实现,换存储结构即可:
- 推荐方案:维护两个独立的字典做字段索引,性能和可维护性最好
// 分别按_s1、_s2建立键索引 Dictionary<string, string> s1Registry = new Dictionary<string, string>(); Dictionary<string, string> s2Registry = new Dictionary<string, string>(); // 添加元素时同时写入两个索引,按需处理字段重复的业务逻辑 s1Registry.TryAdd(key._s1, "12"); s2Registry.TryAdd(key._s2, "12"); s1Registry.TryAdd(key2._s1, "22"); s2Registry.TryAdd(key2._s2, "22"); // 查找时先查s1索引,未命中再查s2索引 bool isFound = s1Registry.ContainsKey(lookUp2._s1) || s2Registry.ContainsKey(lookUp2._s2); - 如果数据量很小(百条级别以内),可以直接用
List<KeyValuePair<Customers, string>>存储,每次查找遍历全集合做OR条件匹配,缺点是时间复杂度为O(n),数据量大时性能差。
内容的提问来源于stack exchange,提问作者Sanju
相关产品推荐
相关产品推荐

