MSIL自身只需声明和使用泛型类型,而无需关心其实例化;.NET是在运行时由CLR来实例化泛型类型的。
跟前面的回答类似,这里用Dictionary<TKey, TVal>举例。
带有未绑定值的泛型参数的泛型类型称为“开放泛型类型”(open generic type)。这可以看作是原始状态、未填值的“模版”。
所有泛型参数都绑定了具体类型的值的泛型类型称为“闭合泛型类型”(closed generic type 或 closed constructed generic type)。
C# / .NET还有“泛型方法”这么一说,本文略过不提,但处理思路跟泛型类型类似。
对Dictionary<TKey, TVal>来说,
CLR在运行时会为一个泛型类型的open generic type和所有constructed type(包括closed与尚未closed的)生成各自独立的元数据(例如MethodTable),用于描述该类型的特征;这些元数据也会有一些共享的部分(例如EEClass)。这样所有泛型类型的反射操作就都可以支持了。例如
注意这个实例化是惰性(lazy)的——只有CLR在运行过程中“遇到”的泛型类型才会对其实例化。
这部分跟C++的泛型类型实例化相似。但这里CLR只是为泛型实例化生成了元数据,还没涉及到代码的特化。
using System; class Program { static void Main() { var g = typeof(System.Collections.Generic.Dictionary<,>); var g1 = g.MakeGenericType(new []{ typeof(string), g.GenericTypeArguments[1] }); var g2 = g.MakeGenericType(new []{ g1.GenericTypeArguments[0], typeof(int) }); Console.WriteLine("{0}, IsGenericType: {1}, IsGenericTypeDefinition: {2}, ContainsGenericParameters: {3}", g, g.IsGenericType, g.IsGenericTypeDefinition, g.ContainsGenericParameters); Console.WriteLine("{0}, IsGenericType: {1}, IsGenericTypeDefinition: {2}, ContainsGenericParameters: {3}", g1, g1.IsGenericType, g1.IsGenericTypeDefinition, g1.ContainsGenericParameters); Console.WriteLine("{0}, IsGenericType: {1}, IsGenericTypeDefinition: {2}, ContainsGenericParameters: {3}", g2, g2.IsGenericType, g2.IsGenericTypeDefinition, g2.ContainsGenericParameters); } }
输出是:
System.Collections.Generic.Dictionary`2[TKey,TValue], IsGenericType: True, IsGenericTypeDefinition: True, ContainsGenericParameters: True System.Collections.Generic.Dictionary`2[System.String,TValue], IsGenericType: True, IsGenericTypeDefinition: False, ContainsGenericParameters: True System.Collections.Generic.Dictionary`2[System.String,System.Int32], IsGenericType: True, IsGenericTypeDefinition: False, ContainsGenericParameters: False
上面的例子里g是generic type definition,是最初始的open generic type;
g1是constructed generic type但尚未close;
g2是closed constructed generic type。
只有close generic type才可以创建对象实例或执行方法的代码。CLR在为泛型类型的方法/泛型方法JIT编译出native code时采用了代码共享的设计:
当然,还得考虑到带有多个泛型参数的泛型类型的情况,但都可以套用上面两条规则去推导。
这么大的功能不可能没有论文,MSR有一篇论文专门讨论CLR的泛型设计与实现:
Design and Implementation of Generics for the .NET Common Language Runtime,请仔细阅读参考。