根据MSDN中==操作符的文档,
For predefined value types, the
equality operator (==) returns true if
the values of its operands are equal,
false otherwise. For reference types
other than string, == returns true if
its two operands refer to the same
object. For the string type, ==
compares the values of the strings.
User-defined value types can overload
the == operator (see operator). So can
user-defined reference types, although
by default == behaves as described
above for both predefined and
user-defined reference types.
那么为什么这个代码片段编译失败呢?
bool Compare<T>(T x, T y) { return x == y; }
我得到错误操作符'=='不能应用于类型为'T'和'T'的操作数。我想知道为什么,因为据我所知==操作符是预定义的所有类型?
编辑:谢谢大家。一开始我并没有注意到这个语句只是关于引用类型的。我还认为为所有值类型提供了逐位比较,现在我知道这是不正确的。
但是,如果我使用引用类型,==操作符是否会使用预定义的引用比较,或者如果类型定义了引用比较,它是否会使用操作符的重载版本?
编辑2:通过反复试验,我们了解到==操作符在使用不受限制的泛型类型时将使用预定义的引用比较。实际上,编译器将使用它可以为受限类型参数找到的最佳方法,但不会再寻找其他方法。例如,下面的代码将始终打印true,即使当Test。test<B>(new B(), new B())调用:
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); } }
在我的例子中,我想对等式运算符进行单元测试。我需要在不显式设置泛型类型的情况下调用相等操作符下的代码。建议对EqualityComparer没有帮助,因为EqualityComparer调用Equals方法,而不是相等操作符。
以下是我如何通过构建LINQ来使用泛型类型。它调用==和!=操作符的正确代码:
/// <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);
}
如果你想确保你的自定义类型的操作符被调用,你可以通过反射来实现。只需使用您的泛型参数获取类型,并为所需的操作符检索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});
这将调用重载操作符,而不是应用在泛型参数上的约束所定义的操作符。可能不实用,但在使用包含几个测试的泛型基类时,可以方便地对操作符进行单元测试。
"...默认情况下==对于预定义引用类型和用户定义引用类型的行为如上所述。”
类型T不一定是引用类型,所以编译器不能做这样的假设。
然而,这将被编译,因为它更显式:
bool Compare<T>(T x, T y) where T : class
{
return x == y;
}
接着另一个问题,“但是,如果我使用引用类型,==操作符会使用预定义的引用比较吗?或者如果类型定义了引用比较,它会使用操作符的重载版本吗?”
我本以为泛型上的==会使用重载版本,但下面的测试证明情况并非如此。有趣的…我很想知道为什么!如果有人知道请分享。
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;
}
导致重载的==运算符被调用。我猜没有指定类型(作为where),编译器不能推断它应该使用重载操作符…尽管我认为即使没有指定类型,它也有足够的信息来做出决定。