虽然这是一个实现细节,但我仍然对.NET运行时如何将类型参数传递给泛型函数感兴趣。
考虑以下C#代码:
using System;
void PrintT<T>() => Console.WriteLine(typeof(T));
PrintT<int>();
PrintT<string>();
PrintT<object>();
字符串
它打印正确的(预期的)输出,因为没有类型擦除:
System.Int32
System.String
System.Object
型
如果我们检查CIL,我们会看到以下操作码:
// in PrintT<T>
IL_0000: ldtoken !!T
IL_0005: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
IL_000a: call void [System.Console]System.Console::WriteLine(object)
IL_000f: nop
// at invocation point
call void Program::'<<Main>$>g__PrintT|0_0'<int32>()
型
正如我所理解的,类型句柄以某种方式传递给函数PrintT<T>
,问题是它如何在解释器和编译代码中实现?更确切地说,如果我们假设没有发生内联,那么某种调用约定是什么。
我希望将它们作为其他参数传递(在.NET托管代码调用约定中将它们前置或追加),或者将它们推入另一个单独的堆栈;这两种情况都发生在呼叫地点。然而,我没有找到任何关于这个主题的讲座,博客文章或论文。
1条答案
按热度按时间lrl1mhuk1#
就IL字节码而言,所见即所得。。它不关心调用约定或任何事情是如何发生的,你可以用笔和纸来实现它所关心的一切。
至于实际的最终JIT ASM汇编代码,这取决于您要具体化的实际类型参数。
如果型别参数是struct(valuetype),则泛型程式码会针对个别型别进行JIT,因此不需要将任何东西传递到任何地方。JITter在创建代码时知道它是什么类型。
但是,如果它是一个引用类型,那么JITter只为所有这样的类型创建一个实现(在许多实现中,
System.__Canon
是一个占位符)。在this paper和on GitHub中有关于它的更多信息。所以在这种情况下,这将取决于。如果从未使用
typeof
,则永远不会传递它。否则,“泛型上下文”将作为隐藏参数添加到本机方法调用中。有关here for the CLR和here for Mono的详细信息,请参阅。