callvirt如何在引擎盖下工作?

aja*_*jay 5 .net clr cil

我试图了解CLR如何实现引用类型和多态.我已经提到了Don Box的Essential .Net第1卷,这对于大部分内容都是很有帮助的.但是当我尝试使用一些IL代码来更好地理解时,我对以下问题感到困惑/困惑.

我会尽力解释这个问题.请考虑以下代码

class Base
{
    public void m()
    {
        Console.WriteLine("Base.m");
    }
}
class Derived : Base
{
    public void m()
    {
        Console.WriteLine("Derived.m");
    }
}
Run Code Online (Sandbox Code Playgroud)

现在考虑一个简单的控制台应用程序,IL的主要方法如下所示.我手动调整了编译器创建的IL,以便用ILAsm.exe再次理解和组装

.class private auto ansi beforefieldinit Console1.Program
       extends [mscorlib]System.Object
{
    .method private hidebysig static void  Main(string[] args) cil managed
    {
      .entrypoint
      // Code size       44 (0x2c)
      .maxstack  1
      .locals init ([0] class Console1.Base d)
      nop
      newobj     instance void Console1.Base::.ctor()
      stloc.0
      ldloc.0
      callvirt   instance void Console1.Derived::m()
      nop
      call       string [mscorlib]System.Console::ReadLine()
      pop
      ret
    } // end of method Program::Main
} // end of class Console1.Program
Run Code Online (Sandbox Code Playgroud)

我期望这段代码不要运行,因为对象引用指向Base的对象,并且基础对象的方法表无法为Derived类中定义的方法m()创建条目.

但神奇地这个代码执行Derived.m()!!

所以,在上面的代码中有两个我不理解的问题:

  1. 以下IL代码中指定的Type的含义是什么?我试图通过将其更改为不同的类型(例如System.Exception !!)进行实验,并且没有报告错误.为什么??

    .locals init([0]类Console1.Base d)

  2. callvirt究竟是如何运作的?如何将调用路由到Derived.m()?

提前致谢!!

此致,Ajay

Cod*_*aos 5

我的猜测是抖动意识到这Derived.m不是虚拟的,因此永远不会指向其他任何地方.所以callvirt减少到空检查和调用而不是通过v表调用.

尝试制作Derived.m虚拟.我打赌它会扔掉.

callvirt即使在调用非虚方法时,C#编译器也会发出指令,如果它无法证明这样,this!=null那么它会得到一个空检查.在这种情况下,抖动足够智能,可以通过固定地址(甚至内联它)的正常呼叫来替换虚拟呼叫.

你应该检查你的代码是否可以验证.我认为不是.