不能运算符==应用于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
约束Equals
从Object.Equals
inheritance,而一个结构的覆盖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。 对于EqualityComparer
build议是没有用的,因为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。 它可以很容易地比较两个对象x
和y
:
static bool IsLessThan(T x, T y) { return ((IComparable)(x)).CompareTo(y) <= 0; }