Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compiler picking wrong overload calling IEquatable<T>.Equals

In a performance sensitive program, I am attempting to explicitly call IEquatable<T>.Equals() and not Object.Equals (to avoid boxing in my case). Despite my best efforts, the compiler is always choosing Object.Equals() instead - which I don't understand. A contrived example class:

class Foo : IEquatable<Foo>
{
    public bool Equals(Foo f)
    {
        Console.WriteLine("IEquatable.Equals");
        return true;
    }

    public override bool Equals(object f)
    {
        Console.WriteLine("Object.Equals");
        return true;
    }
}

Equally contrived code that demonstrates the problem:

// This calls IEquatable<Foo>
Foo f = new Foo();
f.Equals(f);

// This calls Object.Equals
IEquatable<Foo> i = new Foo();
i.Equals(i);

The output of this code is:

IEquatable.Equals
Object.Equals

I read Jon Skeet's article on overloading and came away still not understanding the problem here. So my question is, how do I explicitly call IEquatable<Foo>.Equals on variable i above?

like image 758
Tom19 Avatar asked Feb 03 '16 09:02

Tom19


1 Answers

The reason why the second overload chosen is irrelevant to the caller's type. Rather it's relevant to the type of argument you are passing to Equals. So even if you call f.Equals(i), object.Equals method will be chosen. The reason is simple, compiler looks for the most appropriate overload. Since the IEquatable<Foo> doesn't have to be necessarily Foo, cause there maybe another type let's say Bar which implements IEquatable<Foo>, in that case it wouldn't be right (or possible) to choose Equals(Foo f) overload.

Since the compiler doesn't check the underlying type of IEquatable<Foo>, you need to explicitly cast the parameter to Foo if you want to call Equals(Foo) overload.

like image 189
Selman Genç Avatar answered Oct 31 '22 14:10

Selman Genç