Type of 'this' in abstract class and overloaded method resolution order confusion
-
08-06-2021 - |
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.
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!