Udostępnij za pomocą


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 pierwszy raz skonstruowany z typem wartości jako parametrem, środowisko uruchomieniowe tworzy wyspecjalizowany typ ogólny z podanym parametrem lub parametrami wstawionymi w odpowiednich miejscach w CIL-u. 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 złożony z liczb całkowitych:

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 dzielą wspólnie jedno wystąpienie kodu Stack<int>.

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 long lub struktura zdefiniowana przez użytkownika, jako jej parametr jest tworzona w innym momencie w kodzie. W związku z tym środowisko uruchomieniowe generuje kolejną wersję typu generycznego i zastępuje element long w odpowiednich miejscach w kodzie CIL. 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 typ konstrukcyjny jest tworzony z typem odwołania jako jego parametrem, bez względu na jego rodzaj, środowisko uruchomieniowe ponownie wykorzystuje wcześniej utworzoną wyspecjalizowaną wersję 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, dla typu Stack<T> nie jest tworzona inna wyspecjalizowana wersja klasy Order. Tworzone jest wystąpienie wyspecjalizowanej wersji klasy Stack<T>, a zmienna orders jest ustawiona na odwołanie do niego. 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 Stack<T> utworzonej przy użyciu typu Order, tworzone jest kolejne wystąpienie wyspecjalizowanej klasy Stack<T>. Wskaźniki, które zawiera, są ustawione tak, aby odwoływały się do obszaru pamięci o wielkości typu Customer. 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 klasa ogólna języka C# jest instancjonowana przy użyciu typu wartości lub referencyjnego typu parametru, odbicie może dokonywać zapytań w czasie wykonywania i można ustalić zarówno jej rzeczywisty typ, jak i typ parametru.

Zobacz także