Typy ogólne w środowisku uruchomieniowym (przewodnik programowania w języku C#)

Gdy typ ogólny lub metoda jest kompilowana do wspólnego języka pośredniego (CIL), zawiera metadane identyfikujące je jako mające parametry typu. Sposób użycia CIL dla typu ogólnego zależy od tego, czy podany parametr typu jest typem wartości, czy typem referencyjnym.

Gdy typ ogólny jest najpierw skonstruowany z typem wartości jako parametrem, środowisko uruchomieniowe tworzy wyspecjalizowany typ ogólny z podanym parametrem lub parametrami zastąpionymi we odpowiednich lokalizacjach w wzorniku. Wyspecjalizowane typy ogólne są tworzone jednorazowo dla każdego unikatowego typu wartości, który jest używany jako parametr.

Załóżmy na przykład, że kod programu zadeklarował stos skonstruowany jako liczba całkowita:

Stack<int>? stack;

W tym momencie środowisko uruchomieniowe generuje wyspecjalizowaną wersję Stack<T> klasy, która ma liczbę całkowitą podstawioną odpowiednio dla jego parametru. Teraz, gdy kod programu używa stosu liczb całkowitych, środowisko uruchomieniowe ponownie używa wygenerowanej wyspecjalizowanej Stack<T> klasy. W poniższym przykładzie tworzone są dwa wystąpienia stosu liczb całkowitych i współużytkują jedno wystąpienie Stack<int> kodu:

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

Załóżmy jednak, że inna Stack<T> klasa z innym typem wartości, takim jak struktura zdefiniowana long przez użytkownika, jako jej parametr jest tworzona w innym momencie w kodzie. W związku z tym środowisko uruchomieniowe generuje inną wersję typu ogólnego i zastępuje element long w odpowiednich lokalizacjach w wzorniku. Konwersje nie są już konieczne, ponieważ każda wyspecjalizowana klasa ogólna natywnie zawiera typ wartości.

Typy ogólne działają nieco inaczej w przypadku typów referencyjnych. Przy pierwszym konstruowaniu typu ogólnego przy użyciu dowolnego typu odwołania środowisko uruchomieniowe tworzy wyspecjalizowany typ ogólny z odwołaniami do obiektów zastąpionymi parametrami w CIL. Następnie za każdym razem, gdy skonstruowany typ jest tworzony przy użyciu typu odwołania jako jego parametru, niezależnie od typu, środowisko uruchomieniowe ponownie używa wcześniej utworzonej wyspecjalizowanej wersji typu ogólnego. Jest to możliwe, ponieważ wszystkie odwołania mają ten sam rozmiar.

Załóżmy na przykład, że masz dwa typy odwołań, klasę Customer i klasę Order , a także załóżmy, że utworzono stos Customer typów:

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

W tym momencie środowisko uruchomieniowe generuje wyspecjalizowaną wersję Stack<T> klasy, która przechowuje odwołania do obiektów, które zostaną wypełnione później, zamiast przechowywać dane. Załóżmy, że następny wiersz kodu tworzy stos innego typu odwołania o nazwie Order:

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

W przeciwieństwie do typów wartości inna wyspecjalizowana wersja Stack<T> klasy nie jest tworzona Order dla typu. Zamiast tego tworzone jest wystąpienie wyspecjalizowanej wersji Stack<T> klasy, a zmienna jest ustawiona orders na odwołanie. Załóżmy, że następnie napotkano wiersz kodu w celu utworzenia stosu Customer typu:

customers = new Stack<Customer>();

Podobnie jak w przypadku poprzedniego użycia klasy utworzonej Stack<T> przy użyciu Order typu, tworzone jest inne wystąpienie wyspecjalizowanej Stack<T> klasy. Wskaźniki zawarte w nim są ustawione tak, aby odwoływać się do obszaru pamięci o rozmiarze Customer typu. Ze względu na to, że liczba typów odwołań może się różnić w różny sposób od programu do programu, implementacja języka C# typów ogólnych znacznie zmniejsza ilość kodu przez zmniejszenie do jednej liczby wyspecjalizowanych klas utworzonych przez kompilator dla ogólnych klas typów referencyjnych.

Ponadto, gdy wystąpienie klasy ogólnej języka C# odbywa się przy użyciu typu wartości lub parametru typu odwołania, odbicie może wykonywać zapytania w czasie wykonywania, a zarówno jej rzeczywisty typ, jak i parametr typu można ustalić.

Zobacz też