Search code examples
c#javaoverloadingmethod-resolution-order

Type of 'this' in abstract class and overloaded method resolution order confusion


This very simple example confuses me:

public class X {
    public void XMethod(A a) {
        Console.WriteLine(a.GetType());
        Console.WriteLine("Got A");
    }

    public void XMethod(B b) {
        Console.WriteLine(b.GetType());
        Console.WriteLine("Got B");
    }
}

public abstract class A {
    public virtual void M(X x) {
        Console.WriteLine(this.GetType());
        x.XMethod(this);
    }
}

public class B : A {

}

class Program {
    static void Main(string[] args) {
        X x = new X();
        B b = new B();
        b.M(x);
    }
}

Output of this is

B
B
Got A

Everything up until 'Got A' is fine. I would expect that method X.XMethod(B) would be called when I invoke method M on instance of class B.

What is going on here? Why is XMethod(A) called, and not XMethod(B), when it is clear that type of provided argument is B and not A?

PS: I got same output in java for equivalent implementation.


Solution

  • There is only on A.M method. Not one for A and one for B.

    The IL is the same, in A.M, for all instances; at compile-time, A.M only knows this to be A (or object), hence it resolves always to XMethod(A). This method resolution is in the IL generated at compile-time, and doesn't change for subclasses (indeed, the subclass could be in a separate assembly that the compiler doesn't even know about):

    .method public hidebysig newslot virtual instance void M(class X x) cil managed
    {
        .maxstack 8
        L_0000: ldarg.0 
        L_0001: call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
        L_0006: call void [mscorlib]System.Console::WriteLine(object)
        L_000b: ldarg.1 
        L_000c: ldarg.0 
        L_000d: callvirt instance void X::XMethod(class A)
        L_0012: ret 
    }
    

    To get the behaviour you want, you could use dynamic. Not saying you should, though.