Тип 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!"); }

печать

Строка!
Объект!

Другие вопросы по тегам