JIT 编译器如何编译泛型?

发布于 2024-10-24 06:07:03 字数 159 浏览 3 评论 0原文

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

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

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我乃一代侩神 2024-10-31 06:07:03

我建议阅读C#、Java 和 C++ 中的泛型:与 Anders Hejlsberg 的对话

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

来自采访:

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

[...]

现在,我们要做的是针对所有类型
有价值的实例化
类型 - 例如 ListList
ListList — 我们创建一个
可执行本机的唯一副本
代码。所以 List 得到它自己的代码。
List 拥有自己的代码。
List有自己的代码。对于所有人
我们共享代码的引用类型,
因为他们具有代表性
完全相同的。这只是指针。


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

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

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

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

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

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

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 for List<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. So List<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.

引用类型泛型全部变成相同类型;值类型泛型是单独实例化的。

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

Reference types generics all become the same type; value type generics are instantiated separately.

This is because reference types are all really just Object references (4 or 8 bytes), whereas value types are different and cannot be handled by a single piece of code, due to stack layout differences, etc. Therefore, instantiating multiple copies of a generic type with value types will increase the memory usage by a lot, whereas instantiating multiple copies with reference types won't.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文