C#/ .Net泛型如何知道它们的参数类型?

edA*_*a-y 10 .net c# generics

在C#中,泛型函数或类知道其泛型参数的类型.这意味着动态类型信息,如isas不可用(与Java不同).

我很好奇,编译器如何将这种类型信息提供给泛型方法?对于我可以映像的类,实例可以只是指向类型的指针,但对于泛型函数我不确定,也许只是一个隐藏的参数?

如果泛型保存到IL级别,我相信它们是,那么我想知道在这个级别如何完成.

pho*_*oog 5

由于您已经编辑了问题以将其扩展到C#编译器以外的JIT编译器,因此这里List<T>以此为例概述了该过程.

正如我们已经建立的那样,List<T>该类只有一个IL表示.该表示具有与TC#代码中看到的类型参数对应的类型参数.正如Holger Thiemann在他的评论中所说,当你使用List<>具有给定类型参数的类时,JIT编译器为该类型参数创建类的本机代码表示.

但是,对于引用类型,它仅编译本机代码一次,并将其重用于所有其他引用类型.这是可能的,因为在虚拟执行系统(VES,通常称为"运行时")中,只有一个引用类型,O在规范中调用(参见标准中的第I.12.1节,表I.6:http: //www.ecma-international.org/publications/standards/Ecma-335.htm).此类型定义为"对托管内存的本机大小对象引用".

换句话说,VES的(虚拟)评估堆栈中的所有对象由"对象引用"(实际上是指针)表示,该对象引用本身基本上是无类型的.那么VES如何确保我们不使用不兼容类型的成员?什么阻止我们string.Length在一个实例上调用属性System.Random

为了实施类型安全,VES使用描述每个对象引用的静态类型的元数据,将方法调用接收器的类型与方法的元数据标记所标识的类型进行比较(这也适用于其他成员类型的访问).

例如,要调用对象类的方法,对对象的引用必须位于虚拟评估堆栈的顶部.由于方法的元数据和"堆栈转换"的分析 - 每个IL指令引起的堆栈状态的变化,因此该引用的静态类型是已知的.然后,callcallvirt指令通过包括表示方法的元数据标记来指示要调用的方法,该元数据标记当然指示定义方法的类型.

VES在编译之前"验证"代码,将引用的类型与方法的类型进行比较.如果类型不兼容,则验证失败,程序崩溃.

这对于泛型类型参数也适用于非泛型类型. 为实现此目的,VES限制了可以在类型为无约束泛型类型参数的引用上调用的方法.唯一允许的方法是那些定义的方法System.Object,因为所有对象都是该类型的实例.

对于约束参数类型,该类型的引用可以接收对约束类型定义的方法的调用.例如,如果编写一个方法,您T要从中派生约束类型ICollection,则可以ICollection.Count在类型的引用上调用getter T.VES知道调用此getter是安全的,因为它确保存储到堆栈中该位置的任何引用都将是实现该ICollection接口的某种类型的实例.无论对象的实际类型是什么,JIT编译器都可以使用相同的本机代码.

还要考虑依赖于泛型类型参数的字段.在这种情况下List<T>,有一个类型的数组,T[]用于保存列表中的元素.请记住,实际的内存数组将是一个O对象引用数组.无论数组是a还是a的成员,构造该数组或读取或写入其元素的本机代码看起来都是一样List<string>List<FileInfo>.

因此,在无约束泛型类型的范围内,例如List<T>,T引用与引用一样好System.Object.但是,泛型的优点是VES将类型参数替换为调用者范围中的type参数.换句话说,即使List<string>并且List<FileInfo>内部对待它们的元素相同,调用者也会看到一个Find方法返回a string,而另一个方法返回a FileInfo.

最后,因为所有这些都是通过IL中的元数据实现的,并且因为VES在加载时使用元数据并且JIT编译类型,所以可以在运行时通过反射提取信息.