如何通过JIT编译器编译generics?

我知道generics是由JIT编译的(就像其他所有东西一样),与编译代码时生成的模板形成对比。
问题是可以使用reflection在运行时创建新的generics类型。
这当然会影响通用的约束。 哪个已经通过了语义解析器。

有人能解释一下这是如何处理的吗? 究竟发生了什么?
(代码生成和语义检查)

我建议在C#,Java和C ++中阅读Generics:与Anders Hejlsberg的对话 。

Qn 1.如何通过JIT编译器编译generics?

从采访中:

Anders Hejlsberg:[…]在CLR [公共语言运行时]中,当您编译List或任何其他generics类型时,它会像任何普通类型一样编译为IL [中间语言]和元数据。 当然,IL和元数据包含知道有类型参数的附加信息,但原则上,generics类型只编译任何其他类型编译的方式。 在运行时,当您的应用程序首次引用List时,系统会查看是否有人已经要求List 。 如果没有,它将为List的IL和元数据以及类型参数int提供给JIT。 在JITing IL的过程中,JITer也替换了类型参数。

[…]

现在,我们接下来要做的是所有类型实例化,它们是值类型 – 例如List, List, List, List我们创建可执行本机代码的唯一副本。 因此List获得自己的代码。 List获取自己的代码。 List获取自己的代码。 对于所有引用类型,我们共享代码,因为它们在表示上是相同的。 这只是指针。


Qn 2.事情是可以使用reflection在运行时创建新的generics类型。 这当然会影响通用的约束。 哪个已经通过了语义解析器。 有人能解释一下这是如何处理的吗?

从本质上讲,IL保留了generics类型的高级视图,它允许CLR在运行时检查“动态构造的”generics类型的约束,就像C#编译器可能对C#源代码中的“静态构造”类型一样。编译时间。

这是另一个片段(强调我的):

Anders Hejlsberg:[…]有了约束,你可以提升代码的动态检查,并在编译时或加载时validation它 当你说K必须实现IComparable时,会发生一些事情。 在类型K的任何值上,您现在可以直接访问接口方法而无需强制转换,因为在程序中语义上保证它将实现该接口。 每当您尝试创建该类型的实例化时,编译器将检查您作为K参数提供的任何类型是否实现IComparable,否则您将收到编译时错误。 或者,如果您使用reflection进行操作,则会出现exception。

Bruce Eckel:你说的是编译器和运行时。

Anders Hejlsberg: 编译器会对其进行检查,但您也可以在运行时使用reflection进行检查,然后系统会对其进行检查。 正如我之前所说,在编译时你可以做的任何事情,你也可以在运行时使用reflection。

参考类型generics都变成了同一类型; 值类型generics分别实例化。

这是因为引用类型实际上只是Object引用(4或8个字节),而值类型是不同的,由于堆栈布局的差异等,不能由单个代码处理。因此,实例化通用的多个副本具有值类型的类型将大量增加内存使用量,而使用引用类型实例化多个副本则不会。