2013-01-21 68 views
7

的C#4.0規格如下:運行時類型VS編譯時類型的方法調用

當調用虛擬方法,該實例的用於 運行時類型其中該調用發生確定實際方法 實施調用。在非虛擬方法調用中,實例的編譯時類型是決定性因素。

起初,我認爲這與初始化有關。例如,給定兩個初始化:

BaseClass bcDerived = new Derived(); VS BaseClass bcBase = new BaseClass();

並在一個輔助類的過載:

public virtual void Method(Derived d) 
{ 
    Console.WriteLine("Result = derived called"); 
} 

public virtual void Method(BaseClass d) 
{ 
    Console.WriteLine("Result = base called"); 
} 

Method invokation不是由在此情況下,virtual關鍵字影響。無論標記爲virtual,調用的是派生最少的超載。只有在Derived類中的override期間,方法調用纔會更改。

那麼,「運行時類型」和「編譯時類型」是什麼意思?它們如何影響方法調用?

+0

請說明'Method'的聲明以及使用方法。 –

+0

@BrianRasmussen - 在OP中,這些方法是輔助類的成員。 –

+1

謝謝。在這種情況下,我不確定接受的答案如何與您的問題相匹配,因爲它不包含輔助類。但是,只要你對答案滿意,我就很好。 –

回答

5

這更多的是虛擬與非虛擬方法以及調用的發生方式。您引用的部分規格處理方法調用變量 - 調用bcDerived.SomeMethod(),不調用foo.SomeMethod(bcDerived)

您正在引用的規範是指其中有非虛擬方法的情況下:

public class A 
{ 
    public void Foo() { Console.WriteLine("A.Foo"); } 
    public virtual void Bar() { Console.WriteLine("A.Bar"); } 
} 
public class B : A 
{ 
    public new void Foo() { Console.WriteLine("B.Foo"); } 
    public override void Bar() { Console.WriteLine("B.Bar"); } 
} 

然後調用將被確定由編譯器,在編譯時間的方法,這樣做:

A someInst = new B(); 
someInst.Foo(); 

將導致此通過someInst調用A.Foo()不管是什麼子類A的被提及,因爲這我這是一種非虛擬的方法。

但是,如果您有虛擬方法,則編譯器將指定callvirt指令,該指令會將決策移至運行時。這意味着:

someInst.Bar(); 

會打電話的B.Bar(),不A.Bar()

就你而言,你並沒有調用虛擬方法(在規範中指的是),而是在執行標準的方法解析。 C#Spec的7.5.3詳細介紹了Overload解析。在你的情況下,參數列表(bcDerived)由編譯器檢查,看起來被定義爲類型BaseClass。由於參數列表直接匹配參數列表,因此「最佳匹配」將爲public virtual void Method(BaseClass d),因此在編譯時使用。

如果您查看規範,則方法重載解析不會直接使虛方法調用生效 - 它只會查看類型之間的隱式轉換。

1

在此實例中,參數的編譯時間類型將始終用於確定調用哪個超載。虛擬調度依賴於調用方法的對象的運行時類型。

編譯時類型是由編譯器確定的對象類型,運行時類型是代碼執行時的實際類型。要使用你的例子:

BaseClass bcDerived = new Derived() 

的編譯時類型是BaseClass而運行時類型將是Derived

要了解我們需要稍微延長你的類的含義:

class BaseClass 
{ 
    public virtual void SomeMethod() 
    { 
    Console.WriteLine("In base class"); 
    } 
} 

class Derived : BaseClass 
{ 
    public override void SomeMethod() 
    { 
    Console.WriteLine("In derived class"); 
    } 
} 

現在呼籲bcDerived.SomeMethod()將取決於運行時類型的bcDerived至於BaseClass實現是否會被調用或Derived執行將被調用。

Eric Lippert在.Net(其中part one is here)中撰寫了關於虛擬調度的很好的三部分系列,我強烈建議閱讀它們以更充分地理解該主題。

0
Using these two classes as examples: 

public class Parent 
{ 
    public void NonVirtual() 
    { 
     Console.WriteLine("Nonvirtual - Parent"); 
    } 
    public virtual void Virtual() 
    { 
     Console.WriteLine("Virtual - Parent"); 
    } 
} 

public class Child : Parent 
{ 
    public override void Virtual() 
    { 
     Console.WriteLine("Virtual - Child"); 
    } 

    public void NonVirtual() 
    { 
     Console.WriteLine("Nonvirtual - Child"); 
    } 
} 

虛擬和非虛擬之間的差被最清楚地通過此代碼看出:

Parent childAsParent = new Child(); 
childAsParent.Virtual(); 
childAsParent.NonVirtual(); 

此打印:

Virtual - Child 
Nonvirtual - Parent

在它看到虛擬方法的情況下,在運行時,childAsParent的類型是一個孩子,因此執行孩子的定義Virtual。對於非虛擬方法,它看到該變量的編譯時間類型爲Parent,並忽略實際實例爲Child並使用父級實現的事實。

virtual不用於根據參數的類型確定使用哪種方法的過載。確定調用方法的哪個超載始終在編譯時完成(不使用dynamic時),從不在運行時,所以它總是會選擇Method的超載,在您的示例中,基於編譯時間變量的類型。