1 .對象的創建過程
Bird bird 創建的是一個 Bird 類型的引用,而 new Bird () 完成的是創建 Bird 對象,分配內存空間和初始化操作,然后將這個對象引用賦給 bird 變量,也就是建立 bird 變量與 Bird 對象的關聯。
2 .從繼承的角度分析對象的創建過程
在此我們以 Chicken 對象的創建為例,首先是字段,對象一經創建,會首先找到其父類 Bird ,并為其字段分配存儲空間,而 Bird 也會繼續找到其父類 Animal ,為其分配存儲空間,依次類推直到遞歸結束,也就是完成 System.Object 內存分配為止。
思考
通過上面的講述與分析,我們基本上對 .NET 在編譯期的實現原理有了大致的了解,但是還有以下的問題,一定會引起一定的疑惑,那就是: Bird bird2 = new Chicken();
這種情況下, bird2.ShowType 應該返回什么值呢?而 bird2.type 又該是什么值呢?有兩個原則,是 .NET 專門用于解決這一問題的:
關注對象原則: 調用子類還是父類的方法,取決于創建的對象是子類對象還是父類對象,而不是它的引用類型。例如 Bird bird2 = new Chicken() 時,我們關注的是其創建對象為 Chicken 類型,因此子類將繼承父類的字段和方法,或者覆寫父類的虛方法,而不用關注 bird2 的引用類型是否為 Bird 。 引用類型不同的區別決定了不同的對象在方法表中不同的訪問權限。
注意: 根據關注對象原則,那么下面的兩種情況又該如何區別呢?
Bird bird = new Chicken ();
Chicken chicken = new Chicken ();
根據我們上文的分析, bird 對象和 chicken 對象在內存布局上是一樣的,差別就在于其引用指針的類型不同: bird 為 Bird 類型指針,而 chicken 為 Chicken 類型指針。以方法調用為例,不同的類型指針在虛擬方法表中有不同的附加信息作為標志來區別其訪問的地址區域,稱為 offset 。不同類型的指針只能在其特定地址區域內進行執行,子類覆蓋父類時會保證其訪問地址區域的一致性,從而解決了不同的類型訪問具有不同的訪問權限問題。
執行就近原則: 對于同名字段或者方法,編譯器是按照其順序查找來引用的,也就是首先訪問離它創建最近的字段或者方法,例如上例中的 bird ,是 Bird 類型,因此會首先訪問 Bird_type (注意編譯器是不會重新命名的,在此是為區分起見),如果 type 類型設為 public ,則在此將返回 “ Bird ” 值。這也就是為什么在對象創建時必須將字段按順序排列,而父類要先于子類編譯的原因了。
// 經典指令解析之方法調度
using System;
using System.Collections.Generic;
using System.Text;
namespace testCall
{
public class Father
{
public void DoWork()
{
Console .WriteLine( "Father.DoWork()" );
}
public virtual void DoVirtualWork()
{
Console .WriteLine( "Father.DoVirtualWork()" );
}
public virtual void DoVirtualAll()
{
Console .WriteLine( "Father.DoVirtualAll()" );
}
}
public class Son : Father
{
public static void DoStaticWork()
{
Console .WriteLine( "Son.DoStaticWork()" );
}
public new void DoWork()
{
Console .WriteLine( "Son.DoWork()" );
}
public new virtual void DoVirtualWork()
{
base .DoVirtualWork();
Console .WriteLine( "Son.DoVirtualWork()" );
}
public override void DoVirtualAll()
{
Console .WriteLine( "Son.DoVirtualAll()" );
}
}
public class Grandson : Son
{
public override void DoVirtualWork()
{
base .DoVirtualWork();
Console .WriteLine( "Grandson.DoVirtualWork()" );
}
public override void DoVirtualAll()
{
base .DoVirtualAll();
Console .WriteLine( "Grandson.DoVirtualAll()" );
}
}
// 方法調用測試類
class call
{
static void Main( string [] args)
{
Father son = new Son ();
son.DoWork(); // 調用 Father.DoWork()
son.DoVirtualWork(); // 調用 Father.DoVirtualWork()
Son .DoStaticWork(); // 調用 Son 類特有的靜態方法 DoStaticWork()
Father aGrandson = new Grandson ();
aGrandson.DoWork(); // 調用 Father.DoWork()
aGrandson.DoVirtualWork(); // 調用 Father.DoVirtualWork()
aGrandson.DoVirtualAll(); // 調用 Grandson.DoVirtualAll()
Console .ReadKey();
}
}
}
new 隱藏基類成員中同名的成員方法,也就是該方法獨立于基類的方法。派生類同名方法如果未定義為 new 或 override 則默認定義為 new 的方法。
override 表示覆寫,顯式的重寫基類成員,以實現派生類自己的版本。 C++ 派生類中對同名的基類方法的覆寫只需將基類方法聲明為 virtual 。
Son 中定義的 new DoWork() 獨立于 Father 中定義的同名的 DoWork() 方法,對于 Son 對象繼承的 DoWork() 方法被 new 隱藏而不可見。而 override DoVirtualAll() 則生成自己版本的 DoVirtualAll() 方法。
根據以上示意圖很容易分析出以上測試代碼的結果。
說明:
本文摘自《你必須知道的 .NET 》第十五回《繼承本質論》,有改動。
http://www.cnblogs.com/anytao/archive/2007/09/10/must_net_15.html
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
