运行时中的泛型(C# 编程指南)Generics in the Run Time (C# Programming Guide)

泛型类型或方法编译为 Microsoft 中间语言 (MSIL) 时,它包含将其标识为具有类型参数的元数据。When a generic type or method is compiled into Microsoft intermediate language (MSIL), it contains metadata that identifies it as having type parameters. 如何使用泛型类型的 MSIL 根据所提供的类型参数是值类型还是引用类型而有所不同。How the MSIL for a generic type is used differs based on whether the supplied type parameter is a value type or reference type.

使用值类型作为参数首次构造泛型类型时,运行时创建专用的泛型类型,MSIL 内的适当位置替换提供的一个或多个参数。When a generic type is first constructed with a value type as a parameter, the runtime creates a specialized generic type with the supplied parameter or parameters substituted in the appropriate locations in the MSIL. 为每个用作参数的唯一值类型一次创建专用化泛型类型。Specialized generic types are created one time for each unique value type that is used as a parameter.

例如,假定程序代码声明了一个由整数构造的堆栈:For example, suppose your program code declared a stack that is constructed of integers:

Stack<int> stack;

此时,运行时生成一个专用版 Stack<T> 类,其中用整数相应地替换其参数。At this point, the runtime generates a specialized version of the Stack<T> class that has the integer substituted appropriately for its parameter. 现在,每当程序代码使用整数堆栈时,运行时都重新使用已生成的专用 Stack<T> 类。Now, whenever your program code uses a stack of integers, the runtime reuses the generated specialized Stack<T> class. 在下面的示例中创建了两个整数堆栈实例,且它们共用 Stack<int> 代码的一个实例:In the following example, two instances of a stack of integers are created, and they share a single instance of the Stack<int> code:

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

但是,假定在代码中另一点上再创建一个将不同值类型(例如 long 或用户定义结构)作为参数的 Stack<T> 类。However, suppose that another Stack<T> class with a different value type such as a long or a user-defined structure as its parameter is created at another point in your code. 其结果是,运行时在 MSIL 中生成另一个版本的泛型类型并在适当位置替换 longAs a result, the runtime generates another version of the generic type and substitutes a long in the appropriate locations in MSIL. 转换已不再必要,因为每个专用化泛型类本机包含值类型。Conversions are no longer necessary because each specialized generic class natively contains the value type.

对于引用类型,泛型的作用方式略有不同。Generics work somewhat differently for reference types. 首次使用任意引用类型构造泛型类型时,运行时创建一个专用化泛型类型,用对象引用替换 MSIL 中的参数。The first time a generic type is constructed with any reference type, the runtime creates a specialized generic type with object references substituted for the parameters in the MSIL. 之后,每次使用引用类型作为参数实例化已构造的类型时,无论何种类型,运行时皆重新使用先前创建的专用版泛型类型。Then, every time that a constructed type is instantiated with a reference type as its parameter, regardless of what type it is, the runtime reuses the previously created specialized version of the generic type. 原因可能在于所有引用大小相同。This is possible because all references are the same size.

例如,假定有两个引用类型、一个 Customer 类和一个 Order 类,并假定已创建 Customer 类型的堆栈:For example, suppose you had two reference types, a Customer class and an Order class, and also suppose that you created a stack of Customer types:

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

此时,运行时生成一个专用版 Stack<T> 类,此类存储之后会被填写的引用类型,而不是存储数据。At this point, the runtime generates a specialized version of the Stack<T> class that stores object references that will be filled in later instead of storing data. 假定下一行代码创建另一引用类型的堆栈,其名为 OrderSuppose the next line of code creates a stack of another reference type, which is named Order:

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

不同于值类型,不会为 Order 类型创建 Stack<T> 类的另一专用版。Unlike with value types, another specialized version of the Stack<T> class is not created for the Order type. 相反,创建专用版 Stack<T> 类的实例并将 orders 变量设置为引用此实例。Instead, an instance of the specialized version of the Stack<T> class is created and the orders variable is set to reference it. 假定之后遇到一行创建 Customer 类型堆栈的代码:Suppose that you then encountered a line of code to create a stack of a Customer type:

customers = new Stack<Customer>();

与之前使用通过 Order 类型创建的 Stack<T> 类一样,会创建专用 Stack<T> 类的另一个实例。As with the previous use of the Stack<T> class created by using the Order type, another instance of the specialized Stack<T> class is created. 其中包含的指针设置为引用 Customer 类型大小的内存区。The pointers that are contained therein are set to reference an area of memory the size of a Customer type. 由于引用类型的数量因程序不同而有较大差异,因此通过将编译器为引用类型的泛型类创建的专用类的数量减少至 1,泛型的 C# 实现可极大减少代码量。Because the number of reference types can vary wildly from program to program, the C# implementation of generics greatly reduces the amount of code by reducing to one the number of specialized classes created by the compiler for generic classes of reference types.

此外,使用值类型或引用类型参数实例化泛型 C# 类时,反射可在运行时对其进行查询,且其实际类型和类型参数皆可被确定。Moreover, when a generic C# class is instantiated by using a value type or reference type parameter, reflection can query it at runtime and both its actual type and its type parameter can be ascertained.

请参阅See also