【问题标题】:How do generics get compiled by the JIT compiler?JIT 编译器如何编译泛型?
【发布时间】:2011-07-17 13:46:07
【问题描述】:

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

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

【问题讨论】:

  • 约束不仅由编译器强制执行,抖动也会检查它们。泛型有几个不那么微不足道的方面,它们是如何获得 ngen-ed 的尤其令人难以置信。 确实他们花了 5 年时间。

标签: c# generics compiler-construction jit


【解决方案1】:

我推荐阅读Generics in C#, Java, and C++: A Conversation with Anders Hejlsberg

Qn 1. 泛型是如何编译的 JIT 编译器?

来自采访:

安德斯·海尔斯伯格:[...] 在 CLR [公共语言运行时] 中, 当您编译 List 或任何其他 泛型类型,它编译为 IL [中间语言] 和元数据 就像任何普通类型一样。 IL 和 元数据包含额外的 知道有类型的信息 参数,当然,但在 原则上,泛型类型编译 就像任何其他类型一样 编译。在运行时,当你 应用程序首次引用 到List,系统看一看 如果有人已经要求 List<int>。如果没有,它将馈入 JIT List<T> 的 IL 和元数据 和类型参数 int。 JITer, 在 JITing IL 的过程中,还 替换类型参数。

[...]

现在,我们接下来要做的是针对所有类型 有值的实例化 类型——例如List<int>, List<long>, List<double>, List<float>——我们创建一个 可执行本机的唯一副本 代码。所以List&lt;int&gt; 有自己的代码。 List&lt;long&gt; 有自己的代码。 List&lt;float&gt;获取自己的代码。对所有人 我们共享代码的引用类型, 因为它们具有代表性 完全相同的。这只是指针。


Qn 2. 问题是新的泛型类型 可以通过使用在运行时创建 反射。这当然会影响 泛型的约束。哪一个 已经通过了语义解析器。 有人可以解释这是怎么回事 处理好了吗?

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

这是另一个 sn-p(重点是我的):

安德斯·海尔斯伯格:[...] 有了约束,你可以提升它 动态检查您的代码和 是否在编译时可验证 或加载时间。当你说 K 必须 实现 IComparable,几个 事情发生。在任何类型 K 的值上, 您现在可以直接访问 没有强制转换的接口方法, 因为在程序中语义上 保证它会实施 那个界面。每当你尝试和 创建该类型的实例化, 编译器将检查任何类型 你给作为 K 参数实现 IComparable,否则你得到一个编译 时间错误。 或者,如果您正在使用 反射你得到一个异常。

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

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

【讨论】:

    【解决方案2】:

    引用类型泛型都变成相同类型;值类型泛型单独实例化

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

    【讨论】:

    • 它们不是同一类型。它们为成员函数(方法)共享相同的机器代码......但每个都是不同的类型,具有自己的静态成员变量副本。
    猜你喜欢
    • 2010-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-01
    • 2012-06-05
    相关资源
    最近更新 更多