Search code examples
c#.netgenericsoperator-keywordequality-operator

How operator == is selected?


internal static class ChoosingEqOperTest
{
    class A
    {
        public static bool operator ==(A a, A a2) => false;
    }

    class B:A
    {
        public static bool operator ==(B a, B a2) => true;
    }

    static void CheckChosenOper<T>(T x, T x2) where T : A
    {
        Console.WriteLine(x==x2);
    }

    internal static void Do()
    {
        var a  = new A();
        var a2 = new A();
        var b  = new B();
        var b2 = new B();
        CheckChosenOper(a,a2);      // 1. "False"
        CheckChosenOper(b,b2);      // 2. "False" !?
        CheckChosenOper<A>(a,a2);   // 3. "False"
        CheckChosenOper<A>(b, b2);  // 4. "False"
        //CheckChosenOper<B>(a, a2); //Complie Error
        CheckChosenOper<B>(b, b2);  // 5. "False" !?
        Console.WriteLine(a == a2); // 6. "False"
        Console.WriteLine(b == b2); // 7. "True"
        Console.WriteLine(a == b2); // 8. "False"
        Console.WriteLine(b == a2); // 9. "False"
    }
}

Some questions:

A) Why #2 & #5 prints "False"? - I expect operator implementation should be taken from B class in these cases.

B) Am I right: As both #8 & #9 prints "False" - Cosen operator implementation is the 1st found, both factual-arguments types could be casted to its parameters types?

c) What are common rules of choosing operator == implementation?


Solution

    1. You are using static binding here (static binding is the default, unless you use dynamic), so overload resolution occurs at compile time. Therefore, the expression x==x2 must resolve to either the == in A, or the one in B. It can't resolve it to both. If you look closely at what information the compiler has, you'll notice that it can't resolve it to the one in B, because it only knows that T is A or a subclass thereof. T could be A, or a sibling of B, in which case calling the B overload would not work at all. You can't pass an A into a B parameter, can you?

      You can see your expected result by making your generic method dynamic rather than generic:

       static void CheckChosenOper(dynamic x, dynamic x2)
       {
           Console.WriteLine(x==x2);
       }
      
    2. The compiler must resolve to the A overload because one of the arguments is A, and As cannot be implicitly converted to B, so it can't be passed into that parameter.

    3. This is all described in the language spec, in a section called Binary operator overload resolution. But most of it follows the same rules as method resolution, so you might want to just read that instead.