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); } }
==
is not allowed between two operands of the same type. This is true for struct
types (except "pre-defined" types) which do not overload the operator ==
. As a simple example, try this: var map = typeof(string).GetInterfaceMap(typeof(ICloneable)); Console.WriteLine(map == map); /* compile-time error */
var kvp1 = new KeyValuePair<int, int>(); var kvp2 = kvp1;
, then you cannot check kvp1 == kvp2
because KeyValuePair<,>
is a struct, it is not a C# pre-defined type, and it does not overload the operator ==
. Yet an example is given by var li = new List<int>(); var e1 = li.GetEnumerator(); var e2 = e1;
with which you cannot do e1 == e2
(here we have the nested struct List<>.Enumerator
(called "List`1+Enumerator[T]"
by the runtime) which does not overload ==
).
bool
from a void
...
As others have said, it will only work when T is constrained to be a reference type. Without any constraints, you can compare with null, but only null - and that comparison will always be false for non-nullable value types.
Instead of calling Equals, it's better to use an IComparer<T>
- and if you have no more information, EqualityComparer<T>.Default
is a good choice:
public bool Compare<T>(T x, T y)
{
return EqualityComparer<T>.Default.Equals(x, y);
}
Aside from anything else, this avoids boxing/casting.
"...by default == behaves as described above for both predefined and user-defined reference types."
Type T is not necessarily a reference type, so the compiler can't make that assumption.
However, this will compile because it is more explicit:
bool Compare<T>(T x, T y) where T : class
{
return x == y;
}
Follow up to additional question, "But, in case I'm using a reference type, would the the == operator use the predefined reference comparison, or would it use the overloaded version of the operator if a type defined one?"
I would have thought that == on the Generics would use the overloaded version, but the following test demonstrates otherwise. Interesting... I'd love to know why! If someone knows please share.
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);
}
}
}
Output
Inline: Overloaded == called
Generic:
Press any key to continue . . .
Follow Up 2
I do want to point out that changing my compare method to
static bool Compare<T>(T x, T y) where T : Test
{
return x == y;
}
causes the overloaded == operator to be called. I guess without specifying the type (as a where), the compiler can't infer that it should use the overloaded operator... though I'd think that it would have enough information to make that decision even without specifying the type.
Equals
method (not in the ==
operator).
==
between generic types T
and T
, the best overload is found, given what constraints are carried by T
(there's a special rule that it will never box a value-type for this (which would give a meaningless result), hence there must be some constraint guaranteeing it's a reference type). In your Follow Up 2, if you come in with DerivedTest
objects, and DerivedTest
derives from Test
but introduces a new overload of ==
, you will have the "problem" again. Which overload is called, is "burned" into the IL at compile-time.
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)
So many answers, and not a single one explains the WHY? (which Giovanni explicitly asked)...
.NET generics do not act like C++ templates. In C++ templates, overload resolution occurs after the actual template parameters are known.
In .NET generics (including C#), overload resolution occurs without knowing the actual generic parameters. The only information the compiler can use to choose the function to call comes from type constraints on the generic parameters.
==
works for all types be it reference types or value types. That should be the question to which I dont think you answered.
==
doesn't work for all value types. More importantly, it doesn't have the same meaning for all types, so the compiler doesn't know what to do with it.
==
. Can you include that part too in your answer as I guess that's the main point here
The compile can't know T couldn't be a struct (value type). So you have to tell it it can only be of reference type i think:
bool Compare<T>(T x, T y) where T : class { return x == y; }
It's because if T could be a value type, there could be cases where x == y
would be ill formed - in cases when a type doesn't have an operator == defined. The same will happen for this which is more obvious:
void CallFoo<T>(T x) { x.foo(); }
That fails too, because you could pass a type T that wouldn't have a function foo. C# forces you to make sure all possible types always have a function foo. That's done by the where clause.
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);
}
Object.Equals
but instead tests reference equality. For example, Compare("0", 0.ToString())
would return false, since the arguments would be references to distinct strings, both of which have a zero as their only character.
NullReferenceException
could happen.
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);
}
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.
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.
I wrote the following function looking at the latest msdn. It can easily compare two objects x
and y
:
static bool IsLessThan(T x, T y)
{
return ((IComparable)(x)).CompareTo(y) <= 0;
}
return ((IComparable)(x)).CompareTo(y) <= 0;
T
isn't an IComparable
? Won't this throw an invalid cast exception? And if T
is an IComparable
why not just add that to the generic type constraint?
T
actually implements that interface. If it doesn't, then you get an exception. If it does, then it should probably be part of the generic constraint.
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 "==".
==
token is used for two different operators. If for the given operand types there exists a compatible overload of the equality operator, that overload will be used. Otherwise if both operands are reference types that are compatible with each other, a reference comparison will be used. Note that in the Compare
method above the compiler can't tell that the first meaning applies, but can tell the second meaning applies, so the ==
token will use the latter even if T
overloads the equality-check operator (e.g. if it's type String
).
The .Equals()
works for me while TKey
is a generic type.
public virtual TOutputDto GetOne(TKey id)
{
var entity =
_unitOfWork.BaseRepository
.FindByCondition(x =>
!x.IsDelete &&
x.Id.Equals(id))
.SingleOrDefault();
// ...
}
x.Id.Equals
, not id.Equals
. Presumably, the compiler knows something about the type of x
.
I have 2 solutions and they're very simply.
Solution 1: Cast the generic typed variable to object
and use ==
operator.
Example:
void Foo<T>(T t1, T t2)
{
object o1 = t1;
object o2 = t2;
if (o1 == o2)
{
// ...
// ..
// .
}
}
Solution 2: Use object.Equals(object, object)
method.
Example:
void Foo<T>(T t1, T t2)
{
if (object.Equals(t1, t2)
{
// ...
// ..
// .
}
}
Success story sharing