2012-12-11 41 views
0

我有以下的情況,我想知道的CLR如何知道調用哪個方法:CLR如何知道哪個方法調用從抽象基類繼承的派生類?

public abstract class Shape 
{ 
    public abstract String PrintName(); 
} 

public sealed class Square : Shape 
{ 
    public override String PrintName() { return "Square"; } 
} 

public sealed class Circle : Shape 
{ 
    public override String PrintName() { return "Circle"; } 
} 

於是我分別給出形狀:

Shape square = new Square(); 
Shape circle = new Circle(); 
List<Shape> shapes = new List<Shape> { square, circle }; 

foreach (Shape s in shapes) 
{ 
    Console.WriteLine(s.PrintName()); 
} 

// Output: 
// Square 
// Circle 

那麼是什麼讓我們可以調用方法派生類,即使我們正在調用基類型的方法?我很困惑這是如何處理的。

回答

1

當您實例化Shape square = new Square();時,square確實是Square的事實確實是完好無損的。請記住,變量square確實是對真實對象的引用。就像您在這裏一樣,參考類型(在這種情況下,Shape)必須與實例化類型(Square)的繼承層級相同或更高。

實例化後,當編譯器看到square時,它首先知道它是抽象類型Shape,因爲那是引用的類型。所以,它必須是Shape的子類型,因爲你不能實例化一個抽象對象。既然你說new Square();編譯器會知道確切的類型。同樣,一個對象的確切類型不會因爲您將其分配給baser(更多基礎)類型而丟失。

當你調用square.PrintName();,編譯器首先看到的是square宣佈與抽象類型Shape,誰擁有的方法PrintName(),也標誌着抽象。這告訴編譯器去查找子類中相同的確切方法。如果它在子類中找到PrintName(),一切正常 - 將執行正確的功能。如果沒有,你會得到一個錯誤,因爲基類定義中的抽象詞要求你實現它。

+0

我不認爲編譯器的工作是尋找虛擬方法的實現。該工作在運行時完成。虛擬機負責該工作,而不是編譯器。即使你指的是JIT編譯器,它仍然不準確。編譯器只是檢查有問題的類型是否有相應的方法,並將剩餘的工作(動態查找)留給運行時。 –

2

由於CLR的類型安全特性,它確保在創建Foo實例時不會將其視爲Bar,因此在運行時它始終知道對象是什麼類型。因此,當您在形狀上調用PrintName()時,它知道它是處理Square還是Circle。

請注意,由於GetType()是非虛擬的,因此無法重寫,因此您無法欺騙對象的類型。

相關問題