技术开发 频道

CLR系列:浅析泛型的本质

 2. 解析泛型

  还是先看看一段代码:

public void intrGeneric<T>()
{
   Console.WriteLine(
typeof(T));
}
intrGeneric
<int>();
intrGeneric
<object>();
intrGeneric
<string>();

  那么当我们不知道intrGeneric<T>的T的类型的时候,IL是在怎么处理的呢。看看产生的IL:

.method public hidebysig instance void  intrGeneric<T>() cil managed noinlining
{
  
// Code size       18 (0x12)
   .maxstack  8
   IL_0000:  nop
   IL_0001:  ldtoken    
!!T
   IL_0006:  call      
class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
   IL_000b:  call      
void [mscorlib]System.Console::WriteLine(object)
   IL_0010:  nop
   IL_0011:  ret
}
// end of method Generics_CSharp::intrGeneric

  在这里我们可以看到编译器产生的IL是并没有为T指定一个特定的类型,但是输出的结果为

  System.Int32,System.Object,System.String。我们知道typeof()输出的Runtime Type。我们很容易的得出泛型的另外一个特性:运行时本质。由CLR运行时支持,真正的泛型实例化是发生在JIT编译时,生成不同的本地代码。我们再来看看上面的IL代码:ldtoken    !!T,ldtoken指令:将元数据标记转换为其运行时表示形式,并将其推送到计算堆栈上。!!T时编译器生成的一个占位符,工作形式时这样的:第一次编译的时候,首先生成IL代码以及元数据,T只是一个隐藏的符号,这是并没有对泛型类型进行实例化,当JIT编译的时候,将以实际类型替换IL代码和元数据的T符号,并将其转换为本地代码。

   说到这里我们大概对泛型有个大概的了解。Java编译器在编译泛型的时候,会将所有的泛型参数替换为Object,这实际上还是存在装箱拆箱的过程。还有C++的实现也存在一个很大的问题,那就是代码爆炸,C++会为每种类型都要生成自己的一份代码。C#与JAVA比较解决了性能问题,与C++比较,解决了代码爆炸的问题。那么C#是怎么解决代码爆炸的问题,做到代码共享的呢?这也是C#泛型的一个特性。

0
相关文章