[你必須知道的.NET] 第十五回:繼承本質論
[2] [你必須知道的.NET] 第十五回:繼承本質論
系列文章導航:
[你必須知道的.NET] 第四回:后來居上:class和struct
[你必須知道的.NET] 第五回:深入淺出關鍵字---把new說透
[你必須知道的.NET] 第六回:深入淺出關鍵字---base和this
[你必須知道的.NET] 第七回:品味類型---從通用類型系統開始
[你必須知道的.NET] 第八回:品味類型---值類型與引用類型(上)-內存有理
[你必須知道的.NET] 第九回:品味類型---值類型與引用類型(中)-規則無邊
[你必須知道的.NET] 第十回:品味類型---值類型與引用類型(下)-應用征途
[你必須知道的.NET] 第十一回:參數之惑---傳遞的藝術(上)
[你必須知道的.NET] 第十二回:參數之惑---傳遞的藝術(下)
[你必須知道的.NET] 第十三回:從Hello, world開始認識IL
[你必須知道的.NET] 第十四回:認識IL代碼---從開始到現在
[你必須知道的.NET] 第十六回:深入淺出關鍵字---using全接觸
[你必須知道的.NET]第二十二回:字符串駐留(上)---帶著問題思考
[你必須知道的.NET]第三十二回,深入.NET 4.0之,Tuple一二
本文將介紹以下內容:
• 什么是繼承?
• 繼承的實現本質
1. 引言
關于繼承,你是否駕熟就輕,關于繼承,你是否了如指掌。
本文不討論繼承的基本概念,我們回歸本質,從編譯器運行的角度來揭示.NET繼承中的運行本源,來發現子類對象是如何實現了對父類成員與方法的繼承,以最為簡陋的示例來揭示繼承的實質,闡述繼承機制是如何被執行的,這對于更好的理解繼承,是必要且必然的。
2. 分析
下面首先以一個簡單的動物繼承體系為例,來進行說明:
Code
然后,在測試類中創建各個類對象,由于Animal為抽象類,我們只創建Bird對象和Chicken對象。
Code
下面我們從編譯角度對這一簡單的繼承示例進行深入分析,從而了解.NET內部是如何實現我們強調的繼承機制。
(1)我們簡要的分析一下對象的創建過程:
Bird animal = new Bird();
Bird bird創建的是一個Bird類型的引用,而new Bird()完成的是創建Bird對象,分配內存空間和初始化操作,然后將這個對象賦給bird引用,也就是建立bird引用與Bird對象的關聯。
(2)我們從繼承的角度來分析在編譯器編譯期是如何執行對象的創建過程,因為繼承的本質就體現于對象的創建過程。
在此我們以Chicken對象的創建為例,首先是字段,對象一經創建,會首先找到其父類Bird,并為其字段分配存儲空間,而Bird也會繼續找到其父類Animal,為其分配存儲空間,依次類推直到遞歸結束,也就是完成System.Object內存分配為止。我們可以在編譯器中單步執行的方法來大致了解其分配的過程和順序,因此,對象的創建過程是按照順序完成了對整個父類及其本身字段的內存創建,并且字段的存儲順序是由上到下排列,object類的字段排在最前面,其原因是如果父類和子類出現了同名字段,則在子類對象創建時,編譯器會自動認為這是兩個不同的字段而加以區別。
然后,是方法表的創建,必須明確的一點是方法表的創建是類第一次加載到CLR時完成的,在對象創建時只是將其附加成員TypeHandle指向方法列表在Loader Heap上的地址,將對象與其動態方法列表相關聯起來,因此方法表是先于對象而存在的。類似于字段的創建過程,方法表的創建也是父類在先子類在后,原因是顯而易見的,類Chicken生成方法列表時,首先將Bird的所有方法拷貝一份,然后和Chicken本身的方法列表做以對比,如果有覆寫的虛方法則以子類方法覆蓋同名的父類方法,同時添加子類的新方法,從而創建完成Chicken的方法列表。這種創建過程也是逐層遞歸到Object類,并且方法列表中也是按照順序排列的,父類在前子類在后,其原因和字段大同小異,留待讀者自己體味。
結合我們的分析過程,現在將對象創建的過程以簡單的圖例來揭示其在內存中的分配情形,如下: