在 C++ 中,template
声明需要在头文件中(除非您明确声明模板类型),我明白为什么。
我不明白的是,如果 C# 编译器没有.cs
要检查的文件,它如何处理泛型。我怀疑它会检查 CIL,因为这将是一个处理器密集型操作。我知道它能够从元数据中提取类型信息,但是如果泛型函数的代码也被编码,那么对于可执行文件来说将是一个巨大的膨胀。
那么,编译器又是如何使用泛型的呢?
在 C++ 中,template
声明需要在头文件中(除非您明确声明模板类型),我明白为什么。
我不明白的是,如果 C# 编译器没有.cs
要检查的文件,它如何处理泛型。我怀疑它会检查 CIL,因为这将是一个处理器密集型操作。我知道它能够从元数据中提取类型信息,但是如果泛型函数的代码也被编码,那么对于可执行文件来说将是一个巨大的膨胀。
那么,编译器又是如何使用泛型的呢?
我猜您是在询问 C# 编译器如何使用在引用程序集中定义的泛型类型,而不是询问 C# 编译器如何编译定义泛型类型的 .cs 文件(因为您说“如果它没有.cs 文件”)。
泛型类型和成员在 IL 中被编码为泛型。封闭的泛型类型和成员由 jit 编译器在运行时构造。因此,对您的问题的简单回答是“C# 编译器通过发出导致 jit 编译器在运行时构造它们的 IL 来处理泛型类型。”
C# 编译器从程序集中读取有关类型的元数据,无需查看 IL。您可以使用反射编写类似的代码来分析泛型类型。
C# 编译器也没有理由查看方法的实现。
请检查C# 和 Java 中的泛型...和 C++ 中的模板有什么区别?以及类似的搜索结果 ,原因是 C# 不需要知道方法的实现(与 C++ 不同) - C# 中的所有约束都是从类/方法签名中知道的,这与 C++ 模板中的类型只需要匹配主体内使用的方法的名称不同。
让我试着把事情弄清楚……
泛型存储为 IL,包括元数据中的参数和约束。泛型类型也是类型,它们在运行时转换为运行时类型。这是与不存在模板类型而仅存在派生类型的 C++ 的主要区别;在 C# 中,通用类型存在于 DLL 中,但派生类型不存在(直到它们在运行时创建)。
但是,编译器确实会做一些事情。例如,如果您有一个 List.Add(T),那么 IL 中的签名就是这样。但是,如果您调用它,它会使用 List.Add(Foo) 作为方法来解析该方法。例如,在您的 IL 中,它会显示如下内容(加载字段时):
ldfld class [mscorlib]System.List`1<Foo> Bar::variable
请注意,这里是“Foo”而不是“T”。
此外,如果您在泛型定义中对 T 施加约束,则会根据元数据在编译时检查它们。
在这一点上,我想指出一些完全不同但相关的东西。C# 通过延迟生成类型(通常)获得性能。这也与 C++ 非常不同。如果您尝试使用静态构造函数以及何时调用它们,您可以看到这一点。