2010-05-25 79 views
6

我注意到在C#中,與C++不同,您可以將虛擬方法和泛型方法結合使用。例如:C#方法多態與泛型的性能

using System.Diagnostics; 

class Base { 
    public virtual void Concrete() {Debug.WriteLine("base concrete");} 
    public virtual void Generic<T>() {Debug.WriteLine("base generic");} 
} 

class Derived : Base { 
    public override void Concrete() {Debug.WriteLine("derived concrete");} 
    public override void Generic<T>() {Debug.WriteLine("derived generic");} 
} 

class App { 
    static void Main() { 
     Base x = new Derived(); 
     x.Concrete(); 
     x.Generic<PerformanceCounter>(); 
    } 
} 

考慮到任何數量的Generic<T>版本可以被實例化,它看起來並不像標準vtbl方法可以用來解決方法調用,實際上它不是。下面是生成的代碼:似乎根據通用參數,尋找一個動態的vtbl,然後調用到它

 x.Concrete(); 
mov   ecx,dword ptr [ebp-8] 
mov   eax,dword ptr [ecx] 
call  dword ptr [eax+38h] 
     x.Generic<PerformanceCounter>(); 
push  989A38h 
mov   ecx,dword ptr [ebp-8] 
mov   edx,989914h 
call  76A874F1 
mov   dword ptr [ebp-4],eax 
mov   ecx,dword ptr [ebp-8] 
call  dword ptr [ebp-4] 

額外的代碼。有沒有人寫過關於這個實現的細節?與非一般情況相比,它的表現有多好?

回答

3

.NET泛型實現可以輕鬆處理這樣的場景,並且性能非常好。我剛纔寫了一個blog post

Micosoft Research提供的關於CLR如何實現泛型的信息的最佳資源之一是paper

你得到了關於vtable權利的事情。當JIT編譯器絆倒一個時,CLR如何爲泛型類型創建可執行代碼取決於泛型類型參數。對於值類型和引用類型,處理是不同的。

雖然可執行代碼(實例化,本地圖像)在作爲引用類型的所有泛型類型參數的實例化之間共享,但與實例化的實例(對象)關聯的vtable對於具體參數類型是唯一的。

這裏是從上面提到的紙張相關的報價:

4.2 CLR的垃圾收集堆對象表示的對象是由一個虛函數表指針 接着該對象的內容 (例如網絡連接的視場或陣列表示 元件)。虛擬表的 虛擬方法 分派:它包含代碼指針 爲每個定義的方法或 由對象的類繼承。但是 對於簡單的類類型,至少有 這裏有一對一的對應表類型,也可以用 表示對象的類型。當以這種方式使用 vtable時,我們將其稱爲 該類型的類型句柄。在 實現多態性的基礎上 全面專業化,精確的運行時類型的概念 來爲 相同的參數化類型的免費 不同的實例有不同的虛函數表 。但是現在假設代碼是 在不同的 實例之間共享,例如List<string>List<object>。 兩個實例的vtables將是相同的,因此我們需要某種方式來表示在運行時實例化的 。

...

[對於每個實例,我們]通過指針更換虛表指針的組合vtable- 和實例化的結構和重複它[結構]每實例化。

+0

正是我在找的,謝謝! – zildjohn01 2010-05-27 10:41:20

0

對於通用類(或方法)的每次使用,實現.NET泛型的方式,CLR創建該類(或方法)的新實現,並填入通用參數。對於引用類型,它們全部共享一個實現(因爲它們都只是相同大小的指針);由於尺寸各不相同,因此每個結構都有自己的實現。

所以我猜測每個泛型類型/方法的實現都有它自己的vtable,並且該代碼正在執行'查找泛型實現',然後在查找的實現上執行'查找vtable覆蓋'。