C#泛型(MSIL)的内部是怎么实现的?

软件工程师、主攻高级编程语言虚拟机的设计与实现

143 👍 / 5 💬

问题描述

C++中的模板是有编译器进行实例化变成最终可以被直接调用的函数。C#的泛型是MSIL直接支持的,MSIL在实现泛型的时候,就是最终执行的时候也是通过把泛型实例化来运行的吗?

谢谢


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

,请仔细阅读参考。