C# IEqualityComparer<T>转object比较器抛InvalidCastException
核心原因
泛型协变/逆变特性仅对引用类型生效,值类型不支持泛型变体转换。IEqualityComparer<in T>的in标记确实代表该泛型参数支持逆变,但这个能力有明确的适用前提:转换前后的泛型参数必须都是引用类型,且存在合法的引用转换关系。这个限制是.NET运行时层面的统一规则,所有标记了协变/逆变的泛型接口、委托,只要泛型参数位置是值类型,对应的变体转换全部无效——比如你同样无法将IEnumerable<int>直接转换为IEnumerable<object>,哪怕IEnumerable<out T>是协变接口。
你代码中实现的是IEqualityComparer<double>,double是值类型,值类型和object之间的转换属于装箱/拆箱转换,会生成新的托管对象,不满足泛型变体要求的引用同一性(即转换前后引用指向同一内存地址、内存布局完全一致),因此强制转换必然抛出InvalidCastException。
关于自定义class测试失败的说明
如果使用自定义引用类型(class)实现IEqualityComparer<T>后,向IEqualityComparer<object>转换依然失败,属于测试写法错误。引用类型下逆变是可以正常工作的,参考如下可正常运行的示例:
public class Foo { } public class FooEqualityComparer : IEqualityComparer<Foo> { public bool Equals(Foo a, Foo b) => ReferenceEquals(a, b); public int GetHashCode(Foo a) => a.GetHashCode(); } // 以下转换可以正常执行,不会抛出异常 var fooComparer = new FooEqualityComparer(); var objectComparer = (IEqualityComparer<object>)fooComparer;
适配方案
针对当前MVVM场景下必须传入IEqualityComparer<object>、且实际传入值均为double的情况,最直接的方案是写一层薄包装,内部做拆箱后调用原有的值类型比较器即可,包装层开销极低:
public class ObjectWrappedDoubleComparer : IEqualityComparer<object> { private readonly IEqualityComparer<double> _inner; public ObjectWrappedDoubleComparer(IEqualityComparer<double> inner) => _inner = inner; bool IEqualityComparer<object>.Equals(object x, object y) { return _inner.Equals((double)x, (double)y); } int IEqualityComparer<object>.GetHashCode(object obj) { return _inner.GetHashCode((double)obj); } }
使用时直接包装原有比较器实例即可:
var doubleComparer = new DoubleEqualityComparer(); IEqualityComparer<object> objectComparer = new ObjectWrappedDoubleComparer(doubleComparer);
内容的提问来源于stack exchange,提问作者Marnix
相关产品推荐
相关产品推荐

