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.
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.