当我执行以下代码时,ILDASM中显示了以下内容。我怀疑,即使要执行子类方法,为什么ILDASM会显示调用Baseclass方法。是因为实例是BaseClass类型的吗?如果是因为这个原因,那么基类如何访问子类方法?
class BaseClass
{
public virtual void Mover(long a,long b)
{
Console.WriteLine(a*b+"LongBaseClass");
}
}
class ChildClass : BaseClass
{
public override void Mover(long a, long b)
{
Console.WriteLine(a+b+"LongChildClass");
}
public static void Main(string[] args)
{
long a=10,b=20;
BaseClass bcc=new ChildClass();
bcc.Mover(a,b);
}callvirt instance void practiceonly.BaseClass::Mover(int64, int64)发布于 2019-01-21 06:14:36
callvirt IL代码用于虚拟方法,以确保调用正确的覆盖。对于call和callvirt 这里之间的区别,或者更多的技术讨论这里,有一个相当好的解释。
每个类都有一个方法表(称为Virtual或VTable),它包含指向要为类及其父类中定义的每个方法执行的确切代码的指针。它不是通过层次结构为方法的每个重载版本包含一个条目,而是包含每个方法签名的单个方法指针。当类重写虚拟方法时,该类将在方法的槽中获得不同的方法指针。
callvirt和call将方法元数据令牌作为参数,并使用该令牌的属性来确定所需的方法以及如何定位它。callvirt将使用VTable来根据提供的方法元数据定位要使用的方法,始终获得对调用该方法的对象的类型有效的最派生版本。另一方面,call将调用您指定的方法,当您需要执行base.Mover(a, b)而不陷入无限循环时,这是非常有用的。
因此,要找到当前最佳重载,我们只需要原始virtual或abstract定义的元数据,因为所有重写都将占用各自类在VTable中的相同位置。只有当您使用call来调用特定的覆盖时,它才会使用基以外的其他任何内容。
不过,为了让每个人都感到困惑,C#似乎将使用callvirt来调用引用类型上的几乎所有非静态方法,但base.method()类型调用除外。类实例上的所有普通方法调用(包括属性get/set)都使用callvirt。从C# 1999年早期的原型时代起,它就一直是这样的,而且几乎可以肯定(有些模糊语言)是因为callvirt对this指针进行了额外的检查,以确保它不是null。
https://stackoverflow.com/questions/54284114
复制相似问题