views:

5215

answers:

7

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?

void 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 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); } }
+8  A: 

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.

Johannes Schaub - litb
Thanks for the clarification. I didn't know that value types did not support the == operator out of the box.
Hosam Aly
Hosam, i tested with gmcs (mono), and it always compares references. (i.e it doesn't use an optionally defined operator== for T)
Johannes Schaub - litb
There is one caveat with this solution: the operator== cannot be overloaded; [see this StackOverflow question](http://stackoverflow.com/questions/2919232/using-overloaded-operator-in-a-generic-function).
Dimitri C.
+1  A: 

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 "==".

shahkalpesh
Thanks. I believe reference types can also override the operator too. But the failure reason is now clear.
Hosam Aly
+8  A: 

"...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.

Giovanni Galbo
Thanks. I didn't notice that the statement was about reference types only.
Hosam Aly
Re: Follow Up 2: Actually the compiler will link it the best method it finds, which is in this case Test.op_Equal. But if you had a class that derives from Test and overrides the operator, then Test's operator will still be called.
Hosam Aly
I good practice that I would like to point out is that you should always do the actual comparison inside an overridden `Equals` method (not in the `==` operator).
jpbochi
+3  A: 

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);
}
Jon Limjap
u know im a total c# noob. but i think it fails because the compiler doesn't know what to do. since T isn't known yet, what is done depends on the type T if value types would be allowed. for references, the references are just compared regardless of T. if you do .Equals, then .Equal is just called.
Johannes Schaub - litb
but if you do == on a value type, the value type doesn't have to necassary implement that operator.
Johannes Schaub - litb
That'd make sense, litb :) It is possible that user-defined structs do not overload ==, hence the compiler fail.
Jon Limjap
+19  A: 

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.

Jon Skeet
Thanks. I was trying to write a simple wrapper class, so I just wanted to delegate the operation to the actual wrapped member. But knowing of EqualityComparer<T>.Default certainly added value to me. :)
Hosam Aly
Minor aside, Jon; you might want to note the comment re pobox vs yoda on my post.
Marc Gravell
+3  A: 

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)
Marc Gravell
Very interesting library! :)(Side note: May I suggest using the link to www.yoda.arachsys.com, because the pobox one was blocked by the firewall in my workplace? It's possible that others may face the same issue.)
Hosam Aly
Interesting (the pobox/yoda thing). I'll remember that...
Marc Gravell
The idea is that http://pobox.com/~skeet will *always* point to my website - even if it moves elsewhere. I tend to post links via pobox.com for the sake of posterity - but you can *currently* substitute yoda.arachsys.com instead.
Jon Skeet
The problem with pobox.com is that it's a web-based e-mail service (or so the company's firewall says), so it is blocked. That's why I couldn't follow its link.
Hosam Aly
+2  A: 

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.

Recep