Тип this в абстрактном классе и перегруженный порядок разрешения методов
Этот очень простой пример смущает меня:
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);
}
}
Выход этого
B
B
Got A
Все до "получил" хорошо. Я ожидаю, что этот метод X.XMethod(B)
будет вызван, когда я вызываю метод M
на примере класса B
,
Что здесь происходит? Почему XMethod(A)
называется, а не XMethod(B)
когда ясно, что тип предоставленного аргумента B
и не A
?
PS: я получил тот же вывод в Java для эквивалентной реализации.
3 ответа
Есть только на A.M
метод. Не один для A
и один для B
,
ИЛ такой же, в A.M
для всех случаев; во время компиляции, A.M
только знает this
быть A
(или же object
), следовательно, он всегда разрешает XMethod(A)
, Это разрешение метода находится в IL, сгенерированном во время компиляции, и не изменяется для подклассов (действительно, подкласс может быть в отдельной сборке, о которой компилятор даже не знает):
.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
}
Чтобы получить желаемое поведение, вы можете использовать dynamic
, Не говоря, что ты должен, хотя.
this
всегда ссылается на текущий объект, который вызывает операцию... в том случае, если вы хотите вызвать метод B, вам нужно переопределить виртуальную операцию, потому что, если вы не переопределяете ее, ссылаясь только на родительский класс метода..
public class B : A {
public override void M(X x) {
Console.WriteLine(this.GetType());
x.XMethod(this);
}
}
Я не уверен на 100%, но я думаю, что при использовании перегрузки методов с двумя возможными совпадениями типов всегда используется "самый низкий".
РЕДАКТИРОВАТЬ: После комментария hvd я проверил его, и он прав:
Например, следующий пример:
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!"); }
печать
Строка!
Объект!