제네릭 형식 또는 메서드가 CIL(공용 중간 언어)로 컴파일되면 형식 매개 변수가 있는 것으로 식별하는 메타데이터가 포함됩니다. 제네릭 형식의 CIL 사용 방법은 제공된 형식 매개 변수가 값 형식인지 참조 형식인지에 따라 다릅니다.
값 형식을 매개 변수로 사용하여 제네릭 형식을 처음 생성하는 경우 런타임은 제공된 매개 변수 또는 매개 변수가 CIL의 적절한 위치에서 대체된 특수 제네릭 형식을 만듭니다. 특수화된 제네릭 형식은 매개 변수로 사용되는 각 고유 값 형식에 대해 한 번 만들어집니다.
예를 들어 프로그램 코드가 정수로 구성된 스택을 선언했다고 가정합니다.
Stack<int>? stack;
이 시점에서 런타임은 해당 매개 변수로 적절하게 대체된 정수가 있는 특수화된 버전의 Stack<T> 클래스를 생성합니다. 이제 프로그램 코드에서 정수 스택을 사용할 때마다 런타임은 생성된 특수 Stack<T> 클래스를 다시 사용합니다. 다음 예제에서는 정수 스택의 두 인스턴스가 만들어지고 코드의 단일 인스턴스를 Stack<int> 공유합니다.
Stack<int> stackOne = new Stack<int>();
Stack<int> stackTwo = new Stack<int>();
그러나 코드에서 다른 지점에 사용자 정의 구조체와 같은 Stack<T> 종류의 값 형식을 매개 변수로 가지는 또 다른 long 클래스가 만들어지는 경우를 가정해 보겠습니다. 결과적으로 런타임은 제네릭 형식의 다른 버전을 생성하고 CIL의 적절한 위치에 있는 형식 long 을 대체합니다. 각 특수화된 제네릭 클래스에는 기본적으로 값 형식이 포함되어 있으므로 변환이 더 이상 필요하지 않습니다.
제네릭은 참조 형식에 대해 다소 다르게 작동합니다. 제네릭 형식이 모든 참조 형식으로 처음 생성될 때 런타임은 CIL의 매개 변수로 대체되는 개체 참조를 사용하여 특수화된 제네릭 형식을 만듭니다. 그런 다음, 생성된 형식이 어떤 참조 형식으로 매개 변수를 사용해 인스턴스화되든지 간에, 런타임은 이전에 생성한 제네릭 형식의 특수화된 버전을 다시 사용합니다. 이는 모든 참조의 크기가 같기 때문에 가능합니다.
예를 들어, Customer 클래스와 Order 클래스라는 두 개의 참조 형식이 있고, Customer 형식의 스택을 만들었다고 가정합니다.
class Customer { }
class Order { }
Stack<Customer> customers;
이 시점에서 런타임은 데이터를 저장하는 대신 나중에 채울 개체 참조를 저장하는 특수한 버전의 Stack<T> 클래스를 생성합니다. 다음 코드 줄에서 이름이 Order다음과 같은 다른 참조 형식의 스택을 만든다고 가정합니다.
Stack<Order> orders = new Stack<Order>();
값 형식과는 달리, Stack<T> 형식에 대해 Order 클래스의 또 다른 특수 버전은 만들어지지 않습니다. 대신 특수화된 버전의 클래스 인스턴스가 Stack<T> 만들어지고 변수가 해당 클래스를 orders 참조하도록 설정됩니다. 그런 다음, Customer 형식의 스택을 구현하는 코드 줄이 있다고 가정해 봅시다.
customers = new Stack<Customer>();
이전에 사용된 Stack<T> 형식을 사용하여 만든 Order 클래스와 마찬가지로, 다른 전문화된 Stack<T> 클래스의 인스턴스가 생성됩니다. 그 안에 포함된 포인터는 형식 크기의 Customer 메모리 영역을 참조하도록 설정됩니다. 참조 형식의 수는 프로그램마다 크게 다를 수 있으므로 제네릭의 C# 구현은 참조 형식의 제네릭 클래스에 대해 컴파일러에서 만든 특수 클래스 수를 1개로 줄여 코드 양을 크게 줄입니다.
또한 값 형식 또는 참조 형식 매개 변수를 사용하여 제네릭 C# 클래스를 인스턴스화하는 경우 리플렉션은 런타임에 쿼리할 수 있으며 실제 형식과 해당 형식 매개 변수를 모두 확인할 수 있습니다.
참고하십시오
.NET