views:

161

answers:

4

Below is a simple test program that throws a StackOverflowException when Equals is called. I expected the generic Equals that I got from object to call my IEquatable<MyClass>.Equals, but it does not, it calls itself instead. Why? The parameter type seems ok. Why does it call the generic version in the first place? I am confused.

using System;

namespace consapp
{
    class Program
    {
        static void Main(string[] args)
        {
            MyClass x0 = new MyClass("x0");
            MyClass x1 = new MyClass("x");

            Console.WriteLine(x1.Equals(x0));
        }

    }

    internal class MyClass : IEquatable<MyClass>
    {
        public string Name { get; set; }
        public MyClass(string s) { this.Name = s; }
        public override bool Equals(object x) { return this.Equals(x as MyClass); }
        public override int GetHashCode() { return this.Name.ToLowerInvariant().GetHashCode(); }
        bool IEquatable<MyClass>.Equals(MyClass x) { return x != null && this.Name == x.Name; }
    }
}
+7  A: 

IEquatable.Equals is implemented explicitly. You have to cast the class to the interface first to use an explicit implementation:

public override bool Equals(object x) 
{ 
  return (this as IEquatable).Equals(x as MyClass); 
}

Otherwise it will keep on calling itself in an infinite recursion, yielding in a StackoverflowException eventually.

Femaref
thanks. i did not know that explicitly implemented interface methods cannot be called in the way similar to non interface methods.
akonsu
this is the reason you use explicit implementations - so you can have different interfaces implementing the same method name.
Femaref
In this case though, there's no other method with the same signature, and it will make `x.Equals(y)` where both `x` and `y` are `MyClass` needlessly go through the less specific version. I would change the `Equals(MyClass)` to be implicit and change the `Equals(object)` back to how it was.
Jon Hanna
This is completely true, but it wasn't the concern of the question ;)
Femaref
A: 

If you want to call Object's Equals in your Equal override, you should call base.Equals, not this.Equals.

And note that Object's Equals just compares the references, not the contents.

Timores
this is not the cause of the error here, he wants to call the explicit interface implementation.
Femaref
A: 

Try

public override bool Equals(object x) {
    Console.Write("equals! ");
    return this.Equals(x as MyClass);

}
Novikov
+1  A: 

public override bool Equals(object x) { return this.Equals(x as MyClass); }
The above line is causing it.

You will have to change it to

public override bool Equals(object x) 
{ 
    return ((IEquatable<MyClass>)this).Equals(x as MyClass); 
}
shahkalpesh