抽象类中“this”的类型与重载的方法解析顺序混淆

本文关键字:方法 顺序 重载 this 类型 抽象类 | 更新日期: 2023-09-27 18:33:59

这个非常简单的例子让我感到困惑:

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

直到"Got A"一切都很好。我希望当我在类 B 的实例上调用方法 M 时会调用该方法 X.XMethod(B)

这是怎么回事?为什么XMethod(A)被称为,而不是XMethod(B),当很明显提供的参数类型是B而不是A

PS:我在 java 中获得了相同的输出以进行等效实现。

抽象类中“this”的类型与重载的方法解析顺序混淆

只有 on A.M方法。不是一个A,一个是B.

IL在A.M中对所有实例都是相同的;在编译时,A.M只知道thisA(或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!"); }

指纹

字符串!
对象!