C# 使用多态性时重载方法的ILDASM代码

C# 使用多态性时重载方法的ILDASM代码,c#,polymorphism,overriding,C#,Polymorphism,Overriding,当我执行以下代码时,ILDASM中显示了以下内容。我的疑问是,即使将执行子类方法,为什么ILDASM显示调用了基类方法。是因为实例的类型是BaseClass吗?如果这是原因,基类如何访问子类方法 class BaseClass { public virtual void Mover(long a,long b) { Console.WriteLine(a*b+"LongBaseClass"); } } cla

当我执行以下代码时,ILDASM中显示了以下内容。我的疑问是,即使将执行子类方法,为什么ILDASM显示调用了基类方法。是因为实例的类型是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
IL代码用于虚拟方法,以确保调用正确的重写。对于
call
callvirt
之间的区别有一个相当好的解释,或者进行一些更技术性的讨论

每个类都有一个方法表(称为虚拟方法表或VTable),其中包含指向要为类及其父类中定义的每个方法执行的确切代码的指针。它不是为通过层次结构的每个重载版本的方法包含一个条目,而是为每个方法签名包含一个方法指针。当一个类重写一个虚拟方法时,该类在该方法的插槽中获得一个不同的方法指针

callvirt
call
将方法元数据令牌作为参数,并使用该令牌的属性来确定所需的方法以及如何定位它
callvirt
将使用VTable根据提供的方法元数据定位要使用的方法,始终获取对调用该方法的对象类型有效的最派生版本<另一方面,code>call将调用您指定的方法,这在您需要执行
base.Mover(a,b)
而不陷入无限循环时非常有用

因此,为了找到当前的最佳重载,我们只需要原始
虚拟
抽象
定义的元数据,因为所有重写将在VTable中为各自的类占用相同的槽位。只有在使用
call
调用特定覆盖时,它才会使用基以外的任何东西


然而,为了让每个人都感到困惑,似乎
C#
将使用
callvirt
调用引用类型上几乎所有的非静态方法,除了
base.method()
类型调用。类实例上的所有常规方法调用(包括属性get/set)都使用
callvirt
。显然,从C语言的早期原型时代起就一直如此,而且几乎可以肯定(存在一些模糊语言),因为
callvirt
this
指针进行了额外的检查,以确保它不是
null

[E] 即使子类方法将被执行,为什么ILDASM显示基类方法被调用

因为ILDASM在编译时显示代码的外观,并且在运行时解决方法重写问题。不管你是否打电话

BaseClass baseClass = new ChildClass();
baseClass.Mover(42, 42); // Yields 1764LongBaseClass

两者都将转化为

callvirt instance void BaseClass::Mover(int64, int64)
这是因为编译器不知道重写的方法是否存在以及您是否打算调用它

实际调用的方法在运行时根据引用引用的对象确定(在后一个代码示例中,引用的类型为
ChildClass
,而在第一个示例中,它的类型为
BaseClass



†在.NET中,静态编译时多态性动态运行时多态性之间存在差异。方法重写是动态多态性。静态多态性的一个例子是方法重载。

在.NET的早期beta中,使用了常规方法
调用
。但是这样的代码成功了:
MyClass m=null;m、 somethod()。用户抱怨,于是切换成使用
callvirt
,这样类似的代码就会按预期抛出。@Flydog57是的。本质上,它只是添加了一个null
this
检查
call
不起作用。不知道不可为null的引用类型是否会改变这一点?@Corey我的问题仍然没有答案。我的问题不是关于callvirt的。我的问题是,为什么ILDASM显示基类方法在实际调用子方法时被调用。@NVIswaNihar我将展开第二段,这是对您问题的一种回答。谢谢您的回答
ChildClass childClass = new ChildClass();
childClass.Mover(42, 42); // Yields 1764LongChildClass
callvirt instance void BaseClass::Mover(int64, int64)