程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> CLR怎樣實現虛方法的多態調用(1)

CLR怎樣實現虛方法的多態調用(1)

編輯:關於.NET

最近一直對.net framework中,虛方法的調用是如何實現這個問題有些疑惑,在看了Essential .Net 關於Method的那一章和Artech推薦的文章Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects以後,還是一知半解,有些疑惑得不到答案。主要有這些:

父類定義的非虛方法是否在子類中有拷貝?

虛方法是如何實現多態的?

子類繼承父類的虛方法實現是否和繼承非虛方法機制相同?

如果子類隱藏了父類的虛方法,這又是怎樣實現的?

當然問題不止這麼多,關於接口方面還有很多很多疑惑,不過時間有限,一下也沒辦法全部弄清楚, 有時間慢慢研究。我主要使用Windbg工具來跟蹤調試,關於這個工具如何使用,Google一下就會有很多了 。

這些都是我自己研究加上參考資料所得,如果有不對的地方,希望大家討論指出。

首先看下面這段代碼:

 public class Base
  {
    public virtual void VirtualFun1()
    {
      Console.WriteLine("Base.VirtualFun1");
    }
    public void NoneVirtualFun1()
    {
      System.Console.WriteLine("Base.NoneVirtualFun1");
    }
    public virtual void VirtualFun2()
    {
      System.Console.WriteLine("Base.VirtualFun2");
    }
    public virtual void VirtualFun3()
    {
      System.Console.WriteLine("Base.VirtualFun3");
    }
  }

  public class Derived : Base
  {
    public override void VirtualFun1()
    {
      Console.WriteLine("Derived.VirtualFun1");
    }
    public new virtual void VirtualFun2()
    {
      System.Console.WriteLine("Derived.VirtualFun2");
    }
    public virtual void VirtualFun4()
    {
      System.Console.WriteLine("Derived.VirtualFun4");
    }

  }

Base類是基類,它包含三個虛方法VirtualFun1, VirtuaFun2, VirtualFun3和一個非虛方法 NoneVirtualFun1。

Derived繼承Base類,它重寫了VirtualFun1虛方法,隱藏了Base類的VirtualFun2虛方法,然後又增加 了VirtualFun4虛方法。

看看一個Base類的實例在內存中是怎樣排布的:

Object Ref表示某Base實例的引用,它指向在GC Heap中分配的Base對象,這個對象可以分為三部分: 同步塊索引、類型指針和字段。主要來關注類型指針,它指向該類型的Method Table,這其實是在Load Heap中分配的Type類型對象,所有該類型的實例的類型指針都指向同一個Method Table(這裡表示所有 Base對象的類型指針都指向同一個Method Table)。

Method Table裡面包含很多信息,這裡關注有關Method這一區域,(如果想了解更詳細的method table,請參考上面的文章)。

根據在Method Table裡的信息,可以知道它包含9個Method(其實應該有個字段標示有多少個虛方法, 這裡就沒畫了)。接下來就是這些method,它分為兩部分,前面一部分是所有的虛方法,後面的是非虛方 法。因為所有的類型都是繼承自System.Object類,所以前四個方法是Object類的虛方法(ToString, Equals, GetHashCode, Finalize),接著是Base類定義的三個虛方法(VirtualFun1, VirtualFun2, VirtualFun3),最後是Base類的非虛方法NoneVirtualFun1以及默認的構造函數。下面再來看看Derived 類型的Method Table:

仔細對比一下這兩個Method Table,可以發現這樣幾個特點:

Base類中的所有虛方法在Derived類的Method Table中一一對應

Base類中的所有非虛方法在Derived類中的Method Table並沒有拷貝(這一點回答了上面的第一個問題 )

Derived類新增的虛方法都添加到繼承自Base類的虛方法的後面

如果Derived類override Base類的虛方法,它就將該方法指向自身的實現

如果Derived類使用new關鍵字隱藏了Base類虛方法的實現,它就相當於增加了一個虛方法,而不是覆 蓋。

下面看看調用虛方法時如何實現多態,比如有這樣一段代碼

Base b = new Derived();

b.VirtualFun1();

編譯後在我的機器上會生成這樣的匯編代碼:

mov ecx, esi

mov eax, dword ptr[ecx]

call dword ptr [eax + 3ch]

現在來解釋這幾句代碼:mov ecx, esi 是將新構造的對象的地址保存在ecx寄存器中; mov eax, dword ptr[ecx] 表示ecx的值是一個指針(根據上面的圖可以知道對象的頭4個字節保存的是method table的地址),它將method table的地址保存到eax寄存器中,最後call dword ptr[eax + 3ch]。3ch表 示偏移量,它表示該方法相對於該method table的偏址,是在該類型加載到load heap以後確定的。這樣 ,由method table的地址加上method相對與method table的偏移量,就可以唯一確定一個方法。

這樣在調用b.VirtualFun1(); 時,由於b是Derived類的實例,所以根據它指向的托管對象找到的 method table是Derived類型的method table,就能正確調用該方法。因為Derived類中override了 VirtualFun1這個虛方法,所以調用的是Derived類的實現,而如果沒有override基類的虛方法,它就指向 基類的該方法的實現。

由此可以看出,CLR實現虛方法的機制主要是通過類型的method table加上該虛方法相對於method table的偏移量來確定調用具體方法的。一個虛方法在整個繼承體系所有類型對應的method table中的偏 移量是固定的,比如VirtualFunc1在Base類型的method table中的偏移量是3ch,它在Derived類型的 method table中的偏移量也是3ch,如果還有繼承自Derived類的類,也是同樣,利用這種機制就實現了多 態。

結論

每個類型對應一個Method Table

子類的Method Table中包含父類的所有虛方法,而不包含父類的非虛方法

CLR根據對象找到它對應類型的method table,然後根據該虛方法在method table中的偏移量實現多態 調用。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved