`CallVirt`和`New`关键字
本文关键字:关键字 New CallVirt | 更新日期: 2023-09-27 18:27:47
此问题链接到当引用存储在基类变量中时CLR如何正确调用派生类隐藏的方法?
在我的情况下,我使用的是callvirt
指令,而不是call
class BaseClass
{
public void Write()
{
Method();
}
protected virtual void Method()
{
Console.WriteLine("Base - Method");
}
}
class DerivedClass : BaseClass
{
private new void Method()
{
Console.WriteLine("Derived - Method");
}
}
static void Main(string[] args)
{
DerivedClass dc = new DerivedClass();
BaseClass bcdc = new DerivedClass();
dc.Write();
bcdc.Write();
Console.ReadKey(true);
}
输出:
Base - Method
Base - Method
Write
方法的IL代码:
.method public hidebysig instance void Write() cil managed
{
// Code size 9 (0x9)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: callvirt instance void Private_override.Program/BaseClass::Method()
IL_0007: nop
IL_0008: ret
} // end of method BaseClass::Write
我不明白为什么要调用Base
方法。
这里CLR使用callvirt
指令,这意味着它将查找调用变量类型,由于类型是DerivedClass
,而DerivedClass
隐藏BaseClass.Method
,因此堆中DerivedClass的MethodTable中应该只有DerivedClass.Method
。为什么要调用BaseClass.Method
?callvirt
在搜索方法时是否查找特定的override
标志?
因为您没有override
。将new
更改为override
,它将按照您讨论的方式运行(您也必须更改protected
的可访问性,否则它将无法编译)。new
创建了一个不相关的方法,该方法恰好共享一个名称——它不属于基类中其他同名方法的多态性树的一部分。
你不妨问:"为什么这不叫CompletelyDifferentName()
?"
class BaseClass
{
public void Write()
{
Method();
}
protected virtual void Method()
{
Console.WriteLine("Base - Method");
}
}
class DerivedClass : BaseClass
{
private void CompletelyDifferentName()
{
Console.WriteLine("Derived - Method");
}
}
答案是一样的:CompletelyDifferentName
与称为Method
的virtual
(多态)方法无关。new void Method()
也不是——这就是new
在这里的意思。
因为这正是new的工作方式。New表示您的新方法和基类中同名的方法并没有任何共同点。这和用另一个名称在派生类中创建方法是一样的,只是使用相同的名称。也就是说,基类方法write不会尝试调用新的方法,因为它不是基类中方法的新版本,它只是该类的另一个成员。