通用方法如何,何时何地具体化?

dka*_*man 72 .net c# generics

这个问题让我想知道泛型方法的具体实现在哪里实际存在.我试过谷歌,但我没有想出正确的搜索.

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

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>();
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的脑海中,我总是假设在某些时候它会导致实现3个必要的具体实现,这样,在天真的伪修改中,我们将有这个逻辑的具体实现,其中使用的特定类型导致正确的堆栈分配等.

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

查看IL的通用程序,它仍然以泛型类型表示:

.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
Run Code Online (Sandbox Code Playgroud)

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

Ree*_*sey 77

在C#中,运行时本身支持泛型类型和方法的概念.C#编译器不需要实际创建泛型方法的具体版本.

实际的"具体"泛型方法由JIT在运行时创建,并且在IL中不存在.第一次将泛型方法与类型一起使用时,JIT将查看它是否已创建,如果没有,则为该泛型类型构造适当的方法.

这是泛型和C++中的模板之类的基本区别之一.这也是泛型的许多限制的主要原因 - 因为编译器实际上并没有为类型创建运行时实现,所以接口限制是由编译时限制来处理的,这使得泛型比C++中的模板更具限制性.潜在用例 但是,它们在运行时本身支持的事实允许以C++和其他编译时创建的模板实现不支持的方式从库中创建泛型类型和用法.

  • @dkackman我通过http://www.artima.com/intv/generics.html阅读了它. (7认同)
  • 要添加到Reed的最后一段,一个这样的情况是使用泛型(可能来自库),其类型是在编译泛型之后创建的.要用c ++做到这一点,编译器需要模板代码的源代码. (4认同)
  • 很好的答案里德.是否有您所知道的描述此内容的参考资料? (2认同)

Han*_*ant 45

当方法被jitted时,一如既往地创建泛型方法的实际机器代码.此时,抖动首先检查是否有合适的候选人之前被咬过.这种情况非常普遍,具体运行时类型T是引用类型的方法的代码只需要生成一次,并且适用于每个可能的引用类型T.对T的约束确保此机器代码始终有效,以前由C#编译器检查过.

可以为T作为值类型生成附加副本,它们的机器代码不同,因为T值不再是简单指针.

所以是的,在你的情况下,你将最终得到三种不同的方法.该<string>版本可用于任何参考类型,但您没有其他参考类型.而<int><double>版本符合"T的是值类型"的范畴.

否则是一个很好的例子,这些方法的返回值以不同的方式传递给调用者.在x64抖动上,字符串版本返回带有RAX寄存器的值,就像任何返回的指针值一样,int版本返回EAX寄存器,double版本返回XMM0寄存器.