結合IL和Windbg來看.Net調用繼承虛方法的執行順序

先上測試代碼:ide

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestVirt
{
    class Program
    {
        static void Main(string[] args)
        {
            A c1 = new C();
            c1.Foo();

            C c2 = new C();
            c2.Foo();

            Console.ReadLine();
        }
    }

    class A
    {
        public virtual void Foo()
        {
            Console.WriteLine("Call on A.Foo()");
        }
    }

    class B : A
    {
        public override void Foo()
        {
            Console.WriteLine("Call on B.Foo() ");
        }
    }

    class C : B
    {
        public new void Foo()
        {
            Console.WriteLine("Call on C.Foo()");
        }
    }
}
View Code

可能你對C c2 = new C();這個的結果沒有什麼疑問,可是對A c1 = new C();的結果百思不解。呵呵,咱們慢慢來看這個區別,先來看看最終的IL代碼是什麼樣子的:佈局

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // 代碼大小       34 (0x22)
  .maxstack  1
  .locals init ([0] class TestVirt.A c1,
           [1] class TestVirt.C c2)
  IL_0000:  nop
  IL_0001:  newobj     instance void TestVirt.C::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  callvirt instance void TestVirt.A::Foo()
  IL_000d:  nop
  IL_000e:  newobj     instance void TestVirt.C::.ctor()
  IL_0013:  stloc.1
  IL_0014:  ldloc.1
  IL_0015:  callvirt instance void TestVirt.C::Foo()
  IL_001a:  nop
  IL_001b:  call       string [mscorlib]System.Console::ReadLine()
  IL_0020:  pop
  IL_0021:  ret
} // end of method Program::Main

  

根據IL的結果,咱們明顯能夠看到,兩次調用不一樣的地方就在於一個是Call的A的Foo,一個是C的Foo。測試

可是這裏你注意了:咱們new的是一樣的一個對象,他們具備一樣的內存佈局。spa

用WinDbg來看看咱們new出來對象的MethodTable是長什麼樣子的吧:code

看到沒有,這個C對象的方法表裏面同時包括了C本身定義的Foo和上一層次父對象的Foo方法。對象

 

結合IL的結果和C對象的方法表的Dump結果,相信看官已經明白爲啥兩次調用爲啥會用不一樣了吧。blog

 

算了,仍是簡單描述一下吧:首先根據il的結果明顯兩次調用請求的方法是不一樣的;其次,你能夠看到咱們的C對象引用的方法表裏面確實有兩個Foo方法。內存

呵呵,這樣一樣類型的對象對不一樣方法調用的請求是否是就能夠分開了呢?固然是!ci

PS:可能會有人問:爲啥我請求的A.Foo()你這個MethodTable裏面沒有呢?string

腦補下吧哥:被B給override了。

相關文章
相關標籤/搜索