2010-05-25 14 views
6

Ich bemerkte in C#, im Gegensatz zu C++, können Sie virtuelle und generische Methoden kombinieren. Zum Beispiel:Leistung der C# -Methode Polymorphismus mit Generika

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>(); 
    } 
} 

Da ein beliebige Anzahl von Versionen von Generic<T> instanziert werden könnte, sieht es nicht wie der Standard-vtbl Ansatz verwendet werden könnte, Methodenaufrufe zu lösen, und in der Tat ist es nicht. Hier ist der generierte Code:

 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] 

Der zusätzliche Code erscheint eine dynamische vtbl nach den allgemeinen Parametern zu suchen, und dann hinein ruft. Hat jemand über die Besonderheiten dieser Implementierung geschrieben? Wie gut funktioniert es im Vergleich zum nicht-generischen Fall?

Antwort

3

Die .NET-Generics-Implementierung kann ein solches Szenario problemlos und mit sehr guter Leistung verarbeiten. Ich habe eine blog post darüber vor einer Weile geschrieben.

Eine der besten Quellen für die Suche nach Informationen darüber, wie die CLR Generika implementiert, ist diese paper von Micosoft Research.

Sie haben das Ding über die Vtable richtig. Wie die CLR ausführbaren Code für einen generischen Typ erstellt, wenn der JIT-Compiler auf einen stößt, hängt vom generischen Typparameter ab. Die Handhabung unterscheidet sich für Werttypen und Referenztypen.

Während der executable Code (Instanziierung, das native Bild) unter Instanziierungen für alle generischen Typparameter, die Referenztypen sind, gemeinsam genutzt wird, ist die mit einer Instanz (Objekt) der Instanziierung verknüpfte vtable eindeutig für den konkreten Parametertyp.

Hier ist der relevante Zitat aus dem oben erwähnten Papier:

4,2 Objektdarstellung Objekte in dem Garbage Collected Heap des CLR sind durch einen VTable-Zeiger durch den Inhalt des Objekts folgt dargestellt (zB Felder oder Array Elemente). Die vtable Hauptrolle ist virtuelle Methode Dispatch: Es enthält einen Codezeiger für jede Methode, die definiert ist oder geerbt von der Klasse des Objekts. Aber für einfache Klassenarten, mindestens, wo gibt es eine 1: 1 Korrespondenz zwischen Vtables und Klassen, kann es auch verwendet werden, um den Objekttyp darstellen. Wenn die Vtable auf diese Weise verwendet wird, nennen wir sie den Typ-Typ-Handle.In einer Implementierung von Polymorphismus auf volle Spezialisierung basiert, den Begriff der exakten Laufzeittyp kostenlos kommt als unterschiedliche Ausprägungen des gleichen parametrisierte Typ unterschiedliche vtables. Nehmen wir nun an, dass der Code zwischen verschiedenen Konstanten wie List<string> und List<object> aufgeteilt ist. Die VTabellen für die zwei Instanziierungen werden identisch sein, , so dass wir eine Art der Darstellung die Instanziierung zur Laufzeit benötigen.

...

[Für jede Instanziierung wir] Ersetzen Sie den VTable-Zeiger durch einen Zeiger auf einen kombinierten vtable- und-Instanziierung Struktur und duplizieren sie [die Struktur] pro Instanziierung.

+0

Genau das, was ich gesucht habe, danke! – zildjohn01

0

Die Art und Weise, wie .NET-Generics für jede Verwendung einer generischen Klasse (oder Methode) implementiert werden, erstellt die CLR eine neue Implementierung dieser Klasse (oder Methode) mit dem generischen Parameter ausgefüllt. Für Referenztypen alle teilen Sie eine einzige Implementierung (da sie alle nur Zeiger der gleichen Größe sind); Strukturen haben jeweils ihre eigene Implementierung, da die Größen alle unterschiedlich sind.

Also ich vermute, dass jede Implementierung eines generischen Typs/Methode hat seine eigene vtable als auch, und dieser Code macht eine "Lookup generische Implementierung", dann eine "Lookup vtable override" auf der Implementierung findet es.