我能够找到一篇详细讨论此问题的Microsoft Connect 文章:
不幸的是,这种行为是设计使然,并且没有简单的解决方案可以使用可能包含值类型的类型参数。
如果已知类型是引用类型,则在对象上定义的默认重载会测试变量的引用相等性,尽管类型可以指定其自己的自定义重载。编译器根据变量的静态类型确定使用哪个重载(确定不是多态的)。因此,如果您更改示例以将泛型类型参数 T 约束为非密封引用类型(例如 Exception),编译器可以确定要使用的特定重载,并且将编译以下代码:
public class Test<T> where T : Exception
如果已知类型是值类型,则根据使用的确切类型执行特定的值相等测试。这里没有好的“默认”比较,因为引用比较对值类型没有意义,并且编译器不知道要发出哪个特定的值比较。编译器可以发出对 ValueType.Equals(Object) 的调用,但此方法使用反射并且与特定值比较相比效率很低。因此,即使您要在 T 上指定值类型约束,编译器在此处生成的内容也不合理:
public class Test<T> where T : struct
在您介绍的情况下,编译器甚至不知道 T 是值类型还是引用类型,同样没有生成对所有可能类型有效的内容。引用比较对值类型无效,并且对于不重载的引用类型,某种值比较将是意外的。
这是您可以做的...
我已经验证这两种方法都适用于引用和值类型的通用比较:
object.Equals(param, default(T))
或者
EqualityComparer<T>.Default.Equals(param, default(T))
要使用“==”运算符进行比较,您需要使用以下方法之一:
如果 T 的所有情况都派生自已知的基类,则可以使用泛型类型限制让编译器知道。
public void MyMethod<T>(T myArgument) where T : MyBase
然后编译器会识别如何执行操作MyBase
并且不会抛出您现在看到的“运算符'=='不能应用于'T'和'T'类型的操作数”错误。
另一种选择是将 T 限制为任何实现IComparable
.
public void MyMethod<T>(T myArgument) where T : IComparable
然后使用IComparable 接口CompareTo
定义的方法。