Freigeben über


Generika in der Laufzeit (C#-Programmierhandbuch)

Wenn ein generischer Typ oder eine generische Methode in eine allgemeine Zwischensprache (CIL) kompiliert wird, enthält sie Metadaten, die ihn als Typparameter identifizieren. Wie die CIL für einen generischen Typ verwendet wird, unterscheidet sich je nachdem, ob der angegebene Typparameter ein Werttyp oder Einbezugstyp ist.

Wenn ein generischer Typ zuerst mit einem Werttyp als Parameter erstellt wird, erstellt die Laufzeit einen speziellen generischen Typ mit dem bereitgestellten Parameter oder Parametern, die an den entsprechenden Stellen in der CIL ersetzt werden. Spezielle generische Typen werden einmal für jeden eindeutigen Werttyp erstellt, der als Parameter verwendet wird.

Angenommen, Ihr Programmcode hat einen Stapel deklariert, der aus ganzzahligen Zahlen besteht:

Stack<int>? stack;

An diesem Punkt generiert die Laufzeitumgebung eine spezialisierte Version der Stack<T> Klasse, bei der der ganzzahlige Wert passend als Parameter eingesetzt wurde. Wenn ihr Programmcode nun einen Stapel ganzzahliger Zahlen verwendet, verwendet die Laufzeit die generierte spezialisierte Stack<T> Klasse wieder. Im folgenden Beispiel werden zwei Instanzen eines Stapels ganzzahliger Zahlen erstellt, und sie verwenden eine einzelne Instanz des Stack<int> Codes:

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

Angenommen, eine andere Stack<T> Klasse mit einem anderen Werttyp, z. B. einer long oder einer benutzerdefinierten Struktur, wird an einem anderen Punkt im Code erstellt. Daher generiert die Laufzeit eine andere Version des generischen Typs und ersetzt eine long an den entsprechenden Stellen in CIL. Konvertierungen sind nicht mehr erforderlich, da jede spezialisierte generische Klasse den Werttyp nativ enthält.

Generika funktionieren für Referenztypen etwas anders. Wenn ein generischer Typ zum ersten Mal mit einem beliebigen Verweistyp erstellt wird, erstellt die Laufzeit einen speziellen generischen Typ mit Objektverweise, die durch die Parameter in der CIL ersetzt werden. Jedes Mal, wenn ein konstruierter Typ mit einem Verweistyp als Parameter instanziiert wird, unabhängig davon, welcher Typ es ist, verwendet die Laufzeit die zuvor erstellte spezialisierte Version des generischen Typs. Dies ist möglich, da alle Verweise dieselbe Größe aufweisen.

Angenommen, Sie hatten zwei Verweistypen, eine Customer Klasse und eine Order Klasse, und nehmen auch an, dass Sie einen Stapel von Customer Typen erstellt haben:

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

An diesem Punkt generiert die Laufzeit eine spezielle Version der Klasse, in der Stack<T> Objektverweise gespeichert werden, die später ausgefüllt werden, anstatt Daten zu speichern. Angenommen, die nächste Codezeile erstellt einen Stapel eines anderen Bezugstyps, der benannt Orderwird:

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

Im Gegensatz zu Werttypen wird keine andere spezialisierte Version der Stack<T> Klasse für den Order Typ erstellt. Stattdessen wird eine Instanz der spezialisierten Version der Stack<T> Klasse erstellt, und die orders Variable wird so festgelegt, dass auf sie verwiesen wird. Angenommen, Sie haben dann eine Codezeile gefunden, um einen Stapel eines Customer Typs zu erstellen:

customers = new Stack<Customer>();

Wie bei der vorherigen Nutzung der mit dem Stack<T> Typ erstellten Order Klasse wird eine weitere Instanz der spezialisierten Stack<T> Klasse erstellt. Die darin enthaltenen Zeiger werden so festgelegt, dass auf einen Speicherbereich verwiesen wird, der die Größe eines Customer Typs aufweist. Da die Anzahl der Verweistypen von Programm zu Programm stark variieren kann, reduziert die C#-Implementierung von Generika die Code-Menge erheblich, indem sie die vom Compiler für generische Klassen von Verweistypen erstellten speziellen Klassen auf eine reduziert.

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.

Siehe auch