为IEnumerable<T>实现自定义相等比较器时的嵌套类型及CS1929错误问题
嘿,我完全懂你碰到的这个麻烦——你参考@PaulZahra的代码实现了IEnumerable<T>的自定义相等比较器,目标是实现任意深度的值相等检查,但当T本身也是IEnumerable类型时,Dictionary<TKey, TValue>.ContainsKey()只做引用相等判断,根本没用到你的比较器;而你想改用Keys.Contains()传比较器时,又碰到了CS1929错误,提示Dictionary.KeyCollection没有对应的重载对吧?
下面给你两个靠谱的解决方案:
方案1:创建Dictionary时直接绑定自定义比较器(推荐)
Dictionary的相等判断逻辑是在初始化时决定的——如果你在创建Dictionary的时候,把你的自定义EnumerableEqualityComparer<T>传进去,那么后续所有的ContainsKey、添加键值对等操作都会自动用这个比较器做深度值相等检查,完美匹配你的需求。
示例代码:
// 假设你的自定义比较器叫EnumerableEqualityComparer<T> var deepEqualDict = new Dictionary<IEnumerable<int>, string>(new EnumerableEqualityComparer<int>()); // 之后调用ContainsKey就会用你的比较器做深度值判断 var testKey = new List<int> {1,2,3}; deepEqualDict.ContainsKey(testKey); // 会和字典里的键做值相等对比,而非引用
这个方案是最符合Dictionary设计逻辑的,而且性能也比后续手动判断要好。
方案2:用LINQ的Any方法手动判断(适用于已存在的Dictionary)
如果因为某些限制,你没法修改Dictionary的初始化逻辑(比如字典是外部传入的),那可以用LINQ的Any方法,直接调用你的比较器的Equals方法来逐个对比键:
示例代码:
var targetKey = new List<int> {1,2,3}; bool keyExists = myDict.Keys.Any(key => new EnumerableEqualityComparer<int>().Equals(key, targetKey));
额外提示:确保你的比较器支持嵌套IEnumerable
要实现任意深度的值相等,你的自定义比较器需要能递归处理嵌套的IEnumerable类型。比如在Equals方法里,判断元素类型T是否也是IEnumerable,如果是就递归创建对应的比较器来做元素对比:
public class EnumerableEqualityComparer<T> : IEqualityComparer<IEnumerable<T>> { public bool Equals(IEnumerable<T> x, IEnumerable<T> y) { if (ReferenceEquals(x, y)) return true; if (x == null || y == null) return false; // 检查T是否为IEnumerable类型,递归创建对应的比较器 var elementType = typeof(T); if (elementType.GetInterface(nameof(IEnumerable)) != null && elementType.IsGenericType) { var genericArg = elementType.GetGenericArguments()[0]; var comparerType = typeof(EnumerableEqualityComparer<>).MakeGenericType(genericArg); var elementComparer = (IEqualityComparer<T>)Activator.CreateInstance(comparerType); return x.SequenceEqual(y, elementComparer); } // 非嵌套Enumerable,用默认相等比较器 return x.SequenceEqual(y, EqualityComparer<T>.Default); } public int GetHashCode(IEnumerable<T> obj) { if (obj == null) return 0; int hash = 17; foreach (var item in obj) { hash = hash * 31 + (item?.GetHashCode() ?? 0); } return hash; } }
这样不管你的IEnumerable<T>嵌套多少层,都能正确做深度值相等判断了。
内容的提问来源于stack exchange,提问作者Michael Haddad

