问题描述
我知道泛型是由 JIT 编译的(就像其他所有东西一样),与编译代码时生成的模板不同.
问题是可以在运行时使用反射创建新的泛型类型.
这当然会影响泛型的约束.其中已经通过了语义解析器.
有人可以解释这是如何处理的吗?究竟会发生什么?
(代码生成和语义检查)
我推荐阅读 C# 中的泛型、Java 和 C++:与 Anders Hejlsberg 的对话.
<块引用>Qn 1. 泛型是如何编译的JIT 编译器?
来自采访:
<块引用>安德斯·海尔斯伯格:[...]在 CLR [公共语言运行时] 中,当您编译 List 或任何其他泛型类型,它编译为 IL[中间语言] 和元数据就像任何普通类型一样.IL 和元数据包含额外的知道有类型的信息参数,当然,但在原则上,泛型类型编译就像任何其他类型一样编译.在运行时,当你应用程序首次引用到List,系统看一看如果有人已经要求列表
.如果没有,它将馈入JIT List<T>
的 IL 和元数据和类型参数 int.JITer,在 JITing IL 的过程中,还替换类型参数.
[...]
现在,我们接下来要做的是针对所有类型有值的实例化类型——例如 List<int>、List<long>、List<double>, List<float>
——我们创建一个可执行本机的唯一副本代码.所以 List<int>
得到它自己的代码.List<long>
得到它自己的代码.List
获取自己的代码.对所有人我们共享代码的引用类型,因为它们具有代表性完全相同的.这只是指针.
<小时><块引用>
Qn 2. 问题是新的泛型类型可以通过使用在运行时创建反射.这当然会影响泛型的约束.哪个已经通过了语义解析器.有人可以解释这是怎么回事处理好了吗?
本质上,IL 保留了泛型类型的高级视图,这允许 CLR 在运行时检查动态构造"泛型类型的约束,就像 C# 编译器可能对 C# 源代码中的静态构造"类型所做的那样- 编译时的代码.
这是另一个片段(强调我的):
<块引用>安德斯·海尔斯伯格:[...]有了约束,你可以提升它动态检查您的代码和是否在编译时可验证或加载时间.当你说 K 必须实现 IComparable,几个事情发生.在任何类型 K 的值上,您现在可以直接访问没有强制转换的接口方法,因为在程序中语义上保证它会实施那个界面.每当你尝试和创建该类型的实例化,编译器将检查任何类型你给作为 K 参数实现IComparable,否则你得到一个编译时间错误.或者,如果您正在使用反射你得到一个异常.
Bruce Eckel:你说编译器和运行时.
Anders Hejlsberg:编译器检查它,但你也可以在带有反射的运行时,然后系统会检查它. 正如我之前所说,你在编译时可以做的任何事情,您也可以在运行时使用反射.
I know that generics are compiled by JIT (like everything else), in contrast to templates that are generated when you compile the code.
The thing is that new generic types can be created in runtime by using reflection.
Which can of course affect the generic's constraints. Which already passed the semantic parser.
Can someone explain how this is handled ? And what exactly happens ?
(Both the code generation and semantic check)
I recommend reading Generics in C#, Java, and C++: A Conversation with Anders Hejlsberg.
Qn 1. How do generics get compiled by the JIT compiler?
From the interview:
Anders Hejlsberg: [...] In the CLR [Common Language Runtime], when you compile List, or any other generic type, it compiles down to IL [Intermediate Language] and metadata just like any normal type. The IL and metadata contains additional information that knows there's a type parameter, of course, but in principle, a generic type compiles just the way that any other type would compile. At runtime, when your application makes its first reference to List, the system looks to see if anyone already asked for
List<int>
. If not, it feeds into the JIT the IL and metadata forList<T>
and the type argument int. The JITer, in the process of JITing the IL, also substitutes the type parameter.[...]
Now, what we then do is for all type instantiations that are value types—such as
List<int>, List<long>, List<double>, List<float>
—we create a unique copy of the executable native code. SoList<int>
gets its own code.List<long>
gets its own code.List<float>
gets its own code. For all reference types we share the code, because they are representationally identical. It's just pointers.
Qn 2. The thing is that new generic types can be created in runtime by using reflection. Which can of course affect the generic's constraints. Which already passed the semantic parser. Can someone explain how this is handled?
Essentially, IL retains a high-level view of generic types, which allows the CLR to check constraints for 'dynamically constructed' generic types at run-time just like the C# compiler might do for 'statically constructed' types in C# source-code at compile-time.
Here's another snippet (emphasis mine):
Anders Hejlsberg: [...] With a constraint, you can hoist that dynamic check out of your code and have it be verifiable at compile time or load time. When you say K must implement IComparable, a couple of things happen. On any value of type K, you can now directly access the interface methods without a cast, because semantically in the program it's guaranteed that it will implement that interface. Whenever you try and create an instantiation of that type, the compiler will check that any type you give as the K argument implements IComparable, or else you get a compile time error. Or if you're doing it with reflection you get an exception.
Bruce Eckel: You said the compiler and the runtime.
Anders Hejlsberg: The compiler checks it, but you could also be doing it at runtime with reflection, and then the system checks it. As I said before, anything you can do at compile time, you can also do at runtime with reflection.
这篇关于JIT 编译器如何编译泛型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!