泛型参数传递时C# ==和!=运算符重载失效问题咨询
泛型场景下使用重载==运算符的问题解析
这是个挺常见的泛型运算符重载坑,我来给你捋清楚为什么会出现这种情况,以及怎么解决。
为什么重载的运算符没被调用?
你的泛型方法TestGenerics<T>只给T加了class约束——编译器在编译这个方法的时候,根本不知道T具体会是什么类型,也没法确定它是否重载了==运算符。为了保证兼容性,编译器会默认把==解析为object类型的引用相等比较,也就是你反编译看到的(object)a == (object)b,这自然不会触发你自己重载的逻辑。
能不能在泛型场景下使用重载的运算符?当然可以!
有几种不同的方案,你可以根据自己的C#版本和需求选择:
方案1:使用IEquatable<T>接口(兼容所有C#版本)
如果不需要严格用==运算符,而是只要实现值相等判断,最稳妥的方式是让你的类实现IEquatable<T>接口,然后在泛型方法里调用Equals方法:
public class OperatorTest : IEquatable<OperatorTest> { public int Value { get; } public OperatorTest(int value) => Value = value; public static bool operator ==(OperatorTest left, OperatorTest right) { if (ReferenceEquals(left, right)) return true; if (left is null || right is null) return false; return left.Equals(right); } public static bool operator !=(OperatorTest left, OperatorTest right) => !(left == right); public bool Equals(OperatorTest other) { if (other is null) return false; return Value == other.Value; } public override bool Equals(object obj) => Equals(obj as OperatorTest); public override int GetHashCode() => Value.GetHashCode(); } // 泛型方法修改为: void TestGenerics<T>(T a, T b) where T : class, IEquatable<T> { Assert.IsTrue(a.Equals(b)); }
方案2:用dynamic绕开编译时检查(C# 4+)
如果一定要用==运算符,可以把参数转成dynamic类型,让运行时去解析对应的重载运算符:
void TestGenerics<T>(T a, T b) where T : class { Assert.IsTrue((dynamic)a == (dynamic)b); }
⚠️ 注意:这种方式会丢失编译时的类型检查,而且有一定的性能开销,适合小范围场景使用。
方案3:静态抽象接口(C# 11+)
这是最贴合“用重载运算符”需求的方案——利用C# 11引入的静态抽象接口成员,定义一个包含==和!=运算符的接口,让你的类实现它,再给泛型方法加这个接口的约束:
// 定义包含静态抽象运算符的接口 public interface IEquatableWithOperators<T> where T : IEquatableWithOperators<T> { static abstract bool operator ==(T left, T right); static abstract bool operator !=(T left, T right); } // 让你的类实现这个接口 public class OperatorTest : IEquatableWithOperators<OperatorTest> { public int Value { get; } public OperatorTest(int value) => Value = value; public static bool operator ==(OperatorTest left, OperatorTest right) { if (ReferenceEquals(left, right)) return true; if (left is null || right is null) return false; return left.Value == right.Value; } public static bool operator !=(OperatorTest left, OperatorTest right) => !(left == right); public override bool Equals(object obj) => obj is OperatorTest ot && this == ot; public override int GetHashCode() => Value.GetHashCode(); } // 泛型方法添加接口约束 void TestGenerics<T>(T a, T b) where T : class, IEquatableWithOperators<T> { Assert.IsTrue(a == b); // 现在会直接调用你重载的==运算符! }
内容的提问来源于stack exchange,提问作者Robert Pouleijn
相关产品推荐
相关产品推荐

