抽象类中“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 中获得了相同的输出以进行等效实现。
只有 on A.M
方法。不是一个A
,一个是B
.
IL在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!"); }
指纹
字符串!
对象!