generics方法如何,何时何地具体化?

这个问题让我想知道一个通​​用方法的具体实现在哪里实际存在。 我已经尝试过谷歌,但没有提出正确的search。

如果我们拿这个简单的例子:

class Program { public static T GetDefault<T>() { return default(T); } static void Main(string[] args) { int i = GetDefault<int>(); double d = GetDefault<double>(); string s = GetDefault<string>(); } } 

在我的脑海中,我一直认为在某些时候它会导致一个实现,有3个必要的具体实现,这样,在天真的伪压缩,我们将有这个逻辑的具体实现,其中使用的具体types导致正确的堆栈分配等。

 class Program { static void Main(string[] args) { int i = GetDefaultSystemInt32(); double d = GetDefaultSystemFloat64(); string s = GetDefaultSystemString(); } static int GetDefaultSystemInt32() { int i = 0; return i; } static double GetDefaultSystemFloat64() { double d = 0.0; return d; } static string GetDefaultSystemString() { string s = null; return s; } } 

看一下通用程序的IL,它仍然用generics表示:

 .method public hidebysig static !!T GetDefault<T>() cil managed { // Code size 15 (0xf) .maxstack 1 .locals init ([0] !!T CS$1$0000, [1] !!T CS$0$0001) IL_0000: nop IL_0001: ldloca.s CS$0$0001 IL_0003: initobj !!T IL_0009: ldloc.1 IL_000a: stloc.0 IL_000b: br.s IL_000d IL_000d: ldloc.0 IL_000e: ret } // end of method Program::GetDefault 

那么如何以及在什么时候决定一个int,然后是一个double,然后一个string必须分配在堆栈上并返回给调用者? 这是JIT过程的一个操作吗? 我是否完全错误地看着这个?

在C#中,运行时本身支持genericstypes和方法的概念。 C#编译器不需要实际创buildgenerics方法的具体版本。

实际的“具体”generics方法是由JIT在运行时创build的,并不存在于IL中。 第一次使用types的generics方法时,JIT会查看它是否已经创build,如果不是,则为该generics构造合适的方法。

这是generics和C ++中的模板之间的根本区别之一。 这也是generics的许多限制的主要原因 – 因为编译器实际上并没有为types创build运行时实现,所以接口限制是通过编译时间约束来处理的,这使得generics比C ++中的模板更受限制潜在的用例。 但是,运行时本身支持的事实允许以C ++和其他编译时创build的模板实现中不支持的方式创build通用types和库的用法。

一般方法的实际机器代码是一如既往,当方法出现时创build。 在那个时候,抖动首先检查一个合适的候选人是否在之前爆了。 这是非常普遍的情况,其具体运行时间typesT是引用types的方法的代码只需要被生成一次,并且适用于每个可能的引用typesT.对T的约束确保该机器代码总是有效的,以前由C#编译器检查过。

可能会为T的值types生成附加副本,因为T值不再是简单的指针,所以它们的机器码是不同的。

所以是的,在你的情况下,你会最终有三种不同的方法。 <string>版本可用于任何引用types,但您没有其他types。 而<int><double>版本适合“作为值types的T”类别。

否则就是一个很好的例子,这些方法的返回值以不同的方式传递给调用者。 在x64抖动上,string版本与RAX寄存器一起返回值,就像任何返回的指针值一样,int版本返回EAX寄存器,双重版本返回XMM0寄存器。