Generika in der Runtime (C#-Programmierleitfaden)

Beim Kompilieren eines generischen Typs oder einer generischen Methode in Common Intermediate Language (CIL) wird über Metadaten auf das Vorkommen von Typparametern hingewiesen. Die Art der Verwendung von CIL für einen generischen Typ hängt davon ab, ob es sich bei dem übergebenen Typparameter um einen Werttyp oder einen Referenztyp handelt.

Wenn das erste Mal ein generischer Typ mit einem Werttyp als Parameter erstellt wird, erstellt die Laufzeit einen spezialisierten generischen Typ, bei dem übergebene Parameter an den entsprechenden Stellen in CIL ersetzt werden. Spezialisierte generische Typen werden für jeden eindeutigen Werttyp, der als Parameter verwendet wird, einmal erstellt.

Angenommen, im Programmcode wurde ein Stapel deklariert, der aus Integerwerten erstellt wurde:

Stack<int>? stack;

An diesem Punkt generiert die Laufzeit eine spezialisierte Version der Klasse Stack<T>, in der der Parameter durch die entsprechende ganze Zahl ersetzt wird. Bei Verwendung eines Stapels aus ganzen Zahlen wird jetzt immer die generierte spezialisierte Klasse Stack<T> verwendet. Im folgenden Beispiel werden zwei Instanzen eines Stapels aus ganzen Zahlen erstellt, die eine Instanz des Stack<int>-Codes gemeinsam nutzen:

Stack<int> stackOne = new Stack<int>();
Stack<int> stackTwo = new Stack<int>();

Angenommen, dass an anderer Stelle im Code jedoch eine weitere Stack<T>-Klasse erstellt wird, mit einem anderen Werttyp, z.B. long, oder einer benutzerdefinierten Struktur als Parameter. Daraufhin generiert die Laufzeit eine andere Version des generischen Typs und ersetzt die entsprechenden Stellen in CIL durch long. Konvertierungen sind nicht mehr notwendig, da jede spezialisierte generische Klasse den Werttyp nativ enthält.

Bei Referenztypen unterscheidet sich die Funktionsweise von Generics geringfügig. Wenn das erste Mal ein generischer Typ mit einem beliebigen Referenztyp erstellt wird, erstellt die Laufzeit einen spezialisierten generischen Typ, bei dem die Parameter durch Objektverweise in CIL ersetzt werden. Wenn jetzt ein konstruierter Typ mit einem Referenztyp als Parameter instanziiert wird (unabhängig davon, um welchen Typ es sich dabei handelt), wird die zuvor erstellte spezialisierte Version des generischen Typs verwendet. Dies ist möglich, da alle Verweise die gleiche Größe haben.

Angenommen, Sie verfügen über zwei Referenztypen, eine Customer-Klasse und eine Order-Klasse, und Sie haben einen Stapel von Customer-Typen erstellt:

class Customer { }
class Order { }
Stack<Customer> customers;

An dieser Stelle generiert die Laufzeit eine spezialisierte Version der Klasse Stack<T>, die anstelle von Daten Objektverweise speichert, die zu einem späteren Zeitpunkt mit Daten gefüllt werden. Angenommen, die nächste Codezeile erstellt einen Stapel eines anderen Referenztyps mit dem Namen Order:

Stack<Order> orders = new Stack<Order>();

Anders als bei Werttypen wird für den Typ Stack<T> keine weitere spezialisierte Version der Klasse Order erstellt. Stattdessen wird eine Instanz der spezialisierten Version der Klasse Stack<T> erstellt und die Variable orders so festgelegt, dass sie darauf verweist. Angenommen, Sie würden dann auf eine Codezeile stoßen, die einen Stapel des Typs Customer erstellt:

customers = new Stack<Customer>();

Wie auch bei der vorherigen Verwendung der mit dem Typ Order erstellten Klasse Stack<T> wird eine weitere Instanz der spezialisierten Klasse Stack<T> erstellt. Die darin enthaltenen Zeiger werden so festgelegt, dass sie auf einen Arbeitsspeicherbereich von der Größe eines Customer-Typs verweisen. Da die Anzahl der Referenztypen von Programm zu Programm sehr unterschiedlich sein kann, wird bei der C#-Implementierung von Generics eine übermäßige Zunahme des Codeumfangs dadurch verhindert, dass die Anzahl der spezialisierten Klassen, die vom Compiler für generische Klassen von Referenztypen erstellt werden, auf eine reduziert wird.

Weiterhin gilt, dass eine mit einem Werttyp- oder Referenztypparameter instanziierte generische C#-Klasse zur Laufzeit mittels Reflektion abgefragt werden kann. Dabei können sowohl der tatsächliche Typ als auch der Typparameter ermittelt werden.

Weitere Informationen