Search code examples
c#polymorphismequalityunary-operator

Overriding Equals and type casting


In this following example the third evaluation returns false, all good, but the fourth example returns true..
I don't quite understand how this works however, by default Object.Equals compares two references for object equality, and seeing as a and b both point to a unique instance of a string, this should return false, which it does in the third example but not in the fourth.
Now I understand why it returns true in the 2nd example seeing as the .Equals() method is overridden in the string class, but in the fourth example we're casting this string as an object.
So wouldn't it call Object.Equals in this case?

static void Main()
{
    // Create two equal but distinct strings
    string a = new string(new char[] {'h', 'e', 'l', 'l', 'o'});
    string b = new string(new char[] {'h', 'e', 'l', 'l', 'o'});

    Console.WriteLine (a == b); // Returns true
    Console.WriteLine (a.Equals(b)); // Returns true

    // Now let's see what happens with the same tests but
    // with variables of type object
    object c = a;
    object d = b;

    Console.WriteLine (c == d); // Returns false
    Console.WriteLine (c.Equals(d)); // Returns true
}

Solution

  • The clue is the words "by default". string overrides object.Equals with a custom implementation. Since object.Equals is a polymorphic method (virtual / override / etc), the most-derived implementation gets used even if the variable (/expression) type is object.

    ==, however, is not polymorphic; the implementation used depends entirely on the variable (/expression) type. In this case, since the known type is object, the only comparison available is reference equality.

    Perhaps more succinctly:

    class Foo {
        public override string ToString() { return "hi"; }
    }
    //...
    object obj = new Foo();
    string s = obj.ToString(); // this is "hi"
    

    This is the same principle: the most derived overload of a virtual method is used, regardless of the type that the compiler knows about (object in this case).