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?
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);
}
The compiler must resolve to the A
overload because one of the arguments is A
, and A
s cannot be implicitly converted to B
, so it can't be passed into that parameter.
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.