不能运算符==应用于C#中的genericstypes?

根据MSDN中 ==运算符的文档,

对于预定义的值types,如果操作数的值相等,则相等运算符(==)返回true,否则返回false。 对于string以外的引用types,如果其两个操作数引用同一个对象,则==返回true。 对于stringtypes,==比较string的值。 用户定义的值types可以重载==运算符(请参阅运算符)。 因此,用户定义的引用types,尽pipe默认情况下==的行为如上所述的预定义和用户定义的引用types。

那么为什么这段代码片段无法编译?

 void Compare<T>(T x, T y) { return x == y; } 

我得到错误运算符'=='不能应用于types'T'和'T'的操作数 。 我不知道为什么,因为据我所知, ==运算符是预定义的所有types?

编辑:谢谢大家。 起初我没有注意到这个陈述只是关于参考types。 我还认为,所有值types都提供了逐位比较,现在我知道这是正确的。

但是,如果我使用引用types, ==运算符是否使用预定义的引用比较,或者如果types定义了运算符,它会使用运算符的重载版本吗?

编辑2:通过试验和错误,我们了解到,当使用不受限制的genericstypes时, ==运算符将使用预定义的引用比较。 实际上,编译器会使用它可以find的限制types参数的最好的方法,但是不会再看。 例如,即使在Test.test<B>(new B(), new B())时,下面的代码将始终显示为true

 class A { public static bool operator==(A x, A y) { return true; } } class B : A { public static bool operator==(B x, B y) { return false; } } class Test { void test<T>(T a, T b) where T : A { Console.WriteLine(a == b); } } 

“…默认情况下,==对于预定义和用户定义的引用types的行为如上所述。

typesT不一定是引用types,所以编译器不能做这个假设。

但是,这会被编译,因为它更明确:

  bool Compare<T>(T x, T y) where T : class { return x == y; } 

后续追加的问题是,“但是,如果我使用的是引用types,==运算符是否使用预定义的引用比较,或者如果types定义了运算符,它会使用运算符的重载版本吗?

我会认为==上的generics将使用重载的版本,但下面的testing演示,否则。 有趣…我想知道为什么! 如果有人知道请分享。

 namespace TestProject { class Program { static void Main(string[] args) { Test a = new Test(); Test b = new Test(); Console.WriteLine("Inline:"); bool x = a == b; Console.WriteLine("Generic:"); Compare<Test>(a, b); } static bool Compare<T>(T x, T y) where T : class { return x == y; } } class Test { public static bool operator ==(Test a, Test b) { Console.WriteLine("Overloaded == called"); return a.Equals(b); } public static bool operator !=(Test a, Test b) { Console.WriteLine("Overloaded != called"); return a.Equals(b); } } } 

产量

内联:重载==调用

通用:

按任意键继续 。 。 。

跟进2

我想指出,改变我的比较方法

  static bool Compare<T>(T x, T y) where T : Test { return x == y; } 

导致重载的==运算符被调用。 我猜没有指定types(作为一个地方 ),编译器不能推断它应该使用重载操作符…虽然我认为它会有足够的信息来做出决定,即使没有指定types。

正如其他人所说,只有当T被限制为一个参考types时,它才会起作用。 没有任何限制,你可以与空值进行比较,但只能为空 – 对于不可为空的值types,比较总是为false。

不要调用Equals,最好使用IComparer<T> – 如果没有更多的信息, EqualityComparer<T>.Default是一个不错的select:

 public bool Compare<T>(T x, T y) { return EqualityComparer<T>.Default.Equals(x, y); } 

除了其他任何东西,这避免拳击/铸造。

一般来说, EqualityComparer<T>.Default.Equals应该使用任何实现了IEquatable<T> ,或者有一个合理的Equals实现。

但是,如果==Equals由于某种原因而实现不同,那么我在generics操作符上的工作应该是有用的; 它支持(其他)的操作员版本:

  • 相等(T值1,T值2)
  • NotEqual(T值1,T值2)
  • GreaterThan(T值1,T值2)
  • LessThan(T值1,T值2)
  • GreaterThanOrEqual(T值1,T值2)
  • LessThanOrEqual(T值1,T值2)

这么多的答案,而不是一个解释为什么? (Giovanni明确地问过)…

.NETgenerics不像C ++模板。 在C ++模板中,在实际的模板参数已知之后,会发生重载parsing。

在.NETgenerics(包括C#)中,在不知道实际generics参数的情况下,会发生重载parsing。 编译器可以用来select要调用的函数的唯一信息来自generics参数的types约束。

编译无法知道T不能是一个结构(值types)。 所以你必须告诉它,它只能是我认为的参考types:

 bool Compare<T>(T x, T y) where T : class { return x == y; } 

这是因为如果T可能是一个值types,那么可能会出现x == y会生病的情况 – 当一个types没有定义运算符==时。 同样的情况也会发生,比较明显的是:

 void CallFoo<T>(T x) { x.foo(); } 

这也失败了,因为你可以传递一个没有函数foo的typesT. C#强制你确保所有可能的types总是有一个函数foo。 这是由where子句完成的。

似乎没有类别限制:

 bool Compare<T> (T x, T y) where T: class { return x == y; } 

人们应该认识到,尽pipe==运算符中的class约束EqualsObject.Equalsinheritance,而一个结构的覆盖ValueType.Equals

注意:

 bool Compare<T> (T x, T y) where T: struct { return x == y; } 

也给出了相同的编译器错误。

至今我不明白为什么有一个值types相等运算符比较被编译器拒绝。 我知道一个事实,但这是有效的:

 bool Compare<T> (T x, T y) { return x.Equals(y); } 

这里有一个MSDN连接条目

亚历克斯·特纳的回复开始于:

不幸的是,这种行为是由devise,并没有一个简单的解决scheme,使用==可以包含值types的types参数。

如果你想确保自定义types的操作符被调用,你可以通过reflection来完成。 只需使用generics参数获取types,然后检索所需操作符的MethodInfo(例如,op_Equality,op_Inequality,op_LessThan …)。

 var methodInfo = typeof (T).GetMethod("op_Equality", BindingFlags.Static | BindingFlags.Public); 

然后使用MethodInfo的Invoke方法执行运算符,并传入对象作为参数。

 var result = (bool) methodInfo.Invoke(null, new object[] { object1, object2}); 

这将调用你的重载操作符,而不是generics参数上应用的约束所定义的操作符。 可能不实际,但是在使用包含几个testing的通用基类时,可以派上用场的unit testing你的操作符。

那么在我的情况下,我想unit testing相等运算符。 我需要调用相等运算符下的代码,而不需要明确设置genericstypes。 对于EqualityComparerbuild议是没有用的,因为EqualityComparer称为Equals方法,但不是等于运算符。

下面是我如何通过构build一个LINQ处理genericstypes。 它调用==!=操作符的正确代码:

 /// <summary> /// Gets the result of "a == b" /// </summary> public bool GetEqualityOperatorResult<T>(T a, T b) { // declare the parameters var paramA = Expression.Parameter(typeof(T), nameof(a)); var paramB = Expression.Parameter(typeof(T), nameof(b)); // get equality expression for the parameters var body = Expression.Equal(paramA, paramB); // compile it var invokeEqualityOperator = Expression.Lambda<Func<T, T, bool>>(body, paramA, paramB).Compile(); // call it return invokeEqualityOperator(a, b); } /// <summary> /// Gets the result of "a =! b" /// </summary> public bool GetInequalityOperatorResult<T>(T a, T b) { // declare the parameters var paramA = Expression.Parameter(typeof(T), nameof(a)); var paramB = Expression.Parameter(typeof(T), nameof(b)); // get equality expression for the parameters var body = Expression.NotEqual(paramA, paramB); // compile it var invokeInequalityOperator = Expression.Lambda<Func<T, T, bool>>(body, paramA, paramB).Compile(); // call it return invokeInequalityOperator(a, b); } 
bool Compare(T x, T y) where T : class { return x == y; }
bool Compare(T x, T y) where T : class { return x == y; } 

以上将会起作用,因为在用户定义的引用types的情况下,==被照顾了。
在值types的情况下,==可以被覆盖。 在这种情况下,“!=”也应该被定义。

我认为这可能是原因,它不允许使用“==”的generics比较。

我写了下面的函数看最新的MSDN。 它可以很容易地比较两个对象xy

 static bool IsLessThan(T x, T y) { return ((IComparable)(x)).CompareTo(y) <= 0; }