5

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?

Irshad
  • 3,071
  • 5
  • 30
  • 51
  • 6
    A `Foo` **IS** an `IEquatable`. An `IEquatable` **IS NOT** a `Foo`. Does that make it clearer why this works the way it does - specifcally, why `public bool Equals(Foo f)` cannot get called with a `IEquatable`? – RB. Feb 03 '16 at 09:20
  • If you're sure that `i` *is* a `Foo`, then `i.Equals((Foo)i);`. But, of course, if you're that sure of its type, when isn't that the declared type of `i`? – Damien_The_Unbeliever Feb 03 '16 at 09:22
  • 1
    On an unrelated note, can I just say what an awesome first question this is - you've given a clear statement of your problem, reproducible code, the output, a clear statement of the expected output, and finally, demonstrated independent research - have an upvote, and welcome to Stack Overflow :) – RB. Feb 03 '16 at 09:23
  • 1
    To expand on what RB said, there are two `Equals` methods to choose from: `Equals(Foo other)` and `Equals(object obj)`. Since the given argument `i` is typed as `IEquatable`, the object overload has to be chosen. – Pieter Witvoet Feb 03 '16 at 09:24
  • 1
    That is a very good comment, you should have written it as the answer. – Eldar Dordzhiev Feb 03 '16 at 09:25

1 Answers1

5

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.

Selman Genç
  • 100,147
  • 13
  • 119
  • 184
  • Thanks for the great explanation. This makes total sense and has unblocked me. –  Feb 03 '16 at 19:15