Can't operator == be applied to generic types

2018-12-31 06:26发布

According to the documentation of the == operator in 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.

So why does this code snippet fail to compile?

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

I get the error Operator '==' cannot be applied to operands of type 'T' and 'T'. I wonder why, since as far as I understand the == operator is predefined for all types?

Edit: Thanks, everybody. I didn't notice at first that the statement was about reference types only. I also thought that bit-by-bit comparison is provided for all value types, which I now know is not correct.

But, in case I'm using a reference type, would the == operator use the predefined reference comparison, or would it use the overloaded version of the operator if a type defined one?

Edit 2: Through trial and error, we learned that the == operator will use the predefined reference comparison when using an unrestricted generic type. Actually, the compiler will use the best method it can find for the restricted type argument, but will look no further. For example, the code below will always print true, even when Test.test<B>(new B(), new B()) is called:

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); } }

11条回答
妖精总统
2楼-- · 2018-12-31 06:26

It appears that without the class constraint:

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

One should realize that while class constrained Equals in the == operator inherits from Object.Equals, while that of a struct overrides ValueType.Equals.

Note that:

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

also gives out the same compiler error.

As yet I do not understand why having a value type equality operator comparison is rejected by the compiler. I do know for a fact though, that this works:

bool Compare<T> (T x, T y)
{
    return x.Equals(y);
}
查看更多
残风、尘缘若梦
3楼-- · 2018-12-31 06:27

Well in my case I wanted to unit-test the equality operator. I needed call the code under the equality operators without explicitly setting the generic type. Advises for EqualityComparer were not helpful as EqualityComparer called Equals method but not the equality operator.

Here is how I've got this working with generic types by building a LINQ. It calls the right code for == and != operators:

/// <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);
}
查看更多
墨雨无痕
4楼-- · 2018-12-31 06:28

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

The above will work because == is taken care of in case of user-defined reference types.
In case of value types, == can be overridden. In which case, "!=" should also be defined.

I think that could be the reason, it disallows generic comparison using "==".

查看更多
查无此人
5楼-- · 2018-12-31 06:31

There is an MSDN Connect entry for this here

Alex Turner's reply starts with:

Unfortunately, this behavior is by design and there is not an easy solution to enable use of == with type parameters that may contain value types.

查看更多
墨雨无痕
6楼-- · 2018-12-31 06:34

If you want to make sure the operators of your custom type are called you can do so via reflection. Just get the type using your generic parameter and retrieve the MethodInfo for the desired operator (e.g. op_Equality, op_Inequality, op_LessThan...).

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

Then execute the operator using the MethodInfo's Invoke method and pass in the objects as the parameters.

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

This will invoke your overloaded operator and not the one defined by the constraints applied on the generic parameter. Might not be practical, but could come in handy for unit testing your operators when using a generic base class that contains a couple of tests.

查看更多
何处买醉
7楼-- · 2018-12-31 06:35

In general, EqualityComparer<T>.Default.Equals should do the job with anything that implements IEquatable<T>, or that has a sensible Equals implementation.

If, however, == and Equals are implemented differently for some reason, then my work on generic operators should be useful; it supports the operator versions of (among others):

  • Equal(T value1, T value2)
  • NotEqual(T value1, T value2)
  • GreaterThan(T value1, T value2)
  • LessThan(T value1, T value2)
  • GreaterThanOrEqual(T value1, T value2)
  • LessThanOrEqual(T value1, T value2)
查看更多
登录 后发表回答