Question

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.

Was it helpful?

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.

OTHER TIPS

this is always refer to the current object which is invoking the operation...in you case if you want to call the B method you need to override the virtual operation because if you are not overriding it refering to the method parent class only..

public class B : A {  
public override void M(X x) {
         Console.WriteLine(this.GetType());
         x.XMethod(this);
     } 
} 

I'm not 100% shure, but I think when using method overloading with two possible type matches always the "lowest one" is used.

EDIT: After hvd's comment I checked it and he is right:
E.g. the following example:

static void Main(string[] args)
{
    string str = "bla";
    object obj = str;

    DoIt(str);
    DoIt(obj);
}

public static void DoIt(object p) { Console.WriteLine("Object!"); }
public static void DoIt(string p) { Console.WriteLine("String!"); }

prints

String!
Object!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top