Coder Perfect

In C#, why can’t the operator == be used on generic types?

Problem

According to the == operator’s description on MSDN,

So, why isn’t this code snippet compiling?

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

I’m getting an error. The operator ‘==’ is incompatible with operands of type ‘T’ and ‘T’. I’m not sure why, because the == operator is supposed to be predefined for all types, right?

Thank you so much, everyone. At first, I didn’t realize the phrase was solely about reference types. I also assumed that bit-by-bit comparison was available for all value types, which is no longer the case.

But, if I’m using a reference type, will the == operator utilize the predefined reference comparison or the overloaded version if the type defines one?

Edit 2: We discovered that when using an unconstrained generic type, the == operator will apply the predefined reference comparison. Actually, the compiler will use the best restricted type argument method it can find, but it will not explore any further. Even when Test.testB>(new B(), new B()) is used, the code below will always print 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); } }

Asked by Hosam Aly

Solution #1

It will only work if T is limited to be a reference type, as others have stated. You can compare with null without any constraints, but only with null – and that comparison will always be false for non-nullable value types.

Instead of calling Equals, use an IComparerT> – or, if you don’t have any additional data, EqualityComparerT>. The default setting is a decent option:

public bool Compare<T>(T x, T y)
{
    return EqualityComparer<T>.Default.Equals(x, y);
}

This prevents boxing and casting, among other things.

Answered by Jon Skeet

Solution #2

“…for both predefined and user-defined reference types, by default == acts as indicated above.”

The compiler can’t make that assumption since Type T isn’t always a reference type.

However, because it is more explicit, this will compile:

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

“But, if I’m using a reference type, would the == operator utilize the predefined reference comparison, or would it use the overloaded version of the operator if a type defined one?” is a follow-up question.

I had assumed that == on Generics would utilize the overloaded version, but the following test proves me wrong. Interesting… I’m curious as to why. If anyone knows, please let us know.

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

Overloaded == called inline

Generic:

To continue, press any key…

Follow Up 2

I’d want to emphasize that switching my compare approach to

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

The overloaded == operator is invoked as a result. I suppose the compiler can’t deduce that it should use the overloaded operator without specifying the type (as a where), though I’d assume it would have enough information to do so even without specifying the type.

Answered by Giovanni Galbo

Solution #3

EqualityComparerT> is a broad term. Default. Equals should do the job with anything that implements IEquatable, or that has a sensible Equals implementation.

If, for whatever reason, == and Equals are implemented differently, then my work on generic operators should come in handy; it supports the operator versions of (among others):

Answered by Marc Gravell

Solution #4

There are so many explanations, but none of them explain WHY? (which Giovanni explicitly asked)…

Generics in.NET are not the same as templates in C++. Overload resolution occurs in C++ templates after the actual template arguments are known.

Overload resolution occurs in.NET generics (including C#) without knowing the actual generic parameters. The type constraints on the generic parameters are the only information the compiler has to pick which function to invoke.

Answered by Ben Voigt

Solution #5

The compiler has no way of knowing T isn’t a struct (value type). So, I believe you must inform it that it can only be of reference type:

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. It will be the same for this, which is more obvious:

void CallFoo<T>(T x) { x.foo(); }

This also fails because you could give a type T that doesn’t have a foo function. C# requires you to include a function foo for all conceivable types. The where clause takes care of this.

Answered by Johannes Schaub – litb

Post is based on https://stackoverflow.com/questions/390900/cant-operator-be-applied-to-generic-types-in-c