我知道泛型是由JIT编译的(就像其他所有东西一样),与编译代码时生成的模板形成对比。
问题是可以使用反射在运行时创建新的泛型类型
这当然会影响通用的约束。哪个已经通过了语义解析器。
有人能解释一下这是如何处理的吗?究竟发生了什么?
(代码生成和语义检查)
答案 0 :(得分:36)
我建议阅读Generics in C#, Java, and C++: A Conversation with Anders Hejlsberg。
Qn 1.泛型如何被编译 JIT编译器?
从采访中:
Anders Hejlsberg:[...] 在CLR [公共语言运行时]中, 当您编译List或任何其他时 泛型类型,它编译为IL [中间语言]和元数据 就像任何普通类型一样。 IL和 元数据包含其他 知道有类型的信息 参数,当然,但在 原则,泛型类型编译 就像任何其他类型的方式一样 编译。在运行时,当你的 应用程序首次参考 到列表,系统看起来 如果有人已经要求
List<int>
。如果没有,它会进入 JITList<T>
的IL和元数据 和类型参数int。 JITer, 在JITing IL的过程中,也是 替换类型参数。[...]
现在,我们接下来要做的就是所有类型 有价值的实例化 类型 - 例如
List<int>, List<long>, List<double>, List<float>
- 我们创建了一个 可执行本机的唯一副本 码。所以List<int>
获得了自己的代码。List<long>
获取自己的代码。List<float>
获取自己的代码。对全部 我们共享代码的引用类型, 因为它们具有代表性 相同。这只是指针。
Qn 2.事情是新的通用类型 可以使用在运行时创建 反射。哪个当然可以影响 通用的约束。哪一个 已经通过语义解析器。 有人可以解释这是怎么回事 处理?
本质上,IL保留了泛型类型的高级视图,它允许CLR在运行时检查“动态构造的”泛型类型的约束,就像C#编译器可能对C#源中的“静态构造”类型一样-code在编译时。
这是另一个片段(强调我的):
Anders Hejlsberg:[...] 有了约束,你可以提升它 动态检查你的代码和 在编译时可以验证 或加载时间。当你说K必须时 实现IComparable,一对 事情发生。在K的任何值上, 你现在可以直接访问了 没有强制转换的接口方法, 因为在程序中的语义 它保证会实施 那个界面。每当你尝试和 创建该类型的实例化, 编译器会检查任何类型 你给K参数实现了 IComparable,否则你得到一个编译 时间错误。 或者如果您正在使用它 反思你得到例外。
Bruce Eckel:你说过编译器和 运行时。Anders Hejlsberg:编译器检查 它,但你也可以这样做 运行时用反射,然后是 系统检查它。正如我之前所说, 你在编译时可以做的任何事情, 你也可以在运行时使用 反射。
答案 1 :(得分:2)
参考类型泛型都变为相同类型;值类型泛型实例化单独。
这是因为引用类型实际上只是Object
个引用(4或8个字节),而值类型是不同的,由于堆栈布局不同等原因,单个代码不能处理它们。因此使用值类型实例化泛型类型的多个副本将大量增加内存使用量,而使用引用类型实例化多个副本则不会。