"运行时"解析类型引用
- 首先在"C:\Users\LH89\source\repos"目录下,新建Console1工程(C#控制台)
- 实现简单的代码,并编译为程序集(假定名为Program.exe)
using System;
public class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World");
}
}
- 运行应用程序,CLR会加载并初始化自身,读取程序集的CLR头,查找标识了应用程序入口方法(Main)的MethodDefToken,检索MethodDef元数据表找到方法的IL代码在文件中的偏移量,将IL代码JIT编译成本机代码,最后执行本地代码。
- 通过对程序集运行ILDasm.exe并选择“视图/显示字节”,双击树形视图中的Main方法。
- 对这些代码进行JIT编译,CLR会检测所有类型和成员引用,加载它们的定义程序集(如果尚未加载)。
- 上述代码包含对System.Console.WriteLine的引用。具体来说,IL call指令引用了元数据token 0A000011。该token标识MemberRef元数据表(表0A)中的记录项。CLR检查该MemberRef记录项,发现它的字段引用了TypeRef表中的记录项(System.Console类型)。按照TypeRef记录项,CLR被引导至一个AssemblyRef记录项:“mscorlib,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089”,这时CLR就知道了它需要的是哪个程序集。接着,CLR必须定位并加载该程序集。
- 解析引用的类型时,CLR可能在以下三个地方找到类型。
- 相同文件
编译时便能发现对相同文件中的类型的访问,这称为早期绑定。类型直接从文件中加载,执行继续。 - 不同文件,相同程序集
“运行时”确保被引用的文件在当前程序集元数据的FileDef表中,检查加载程序集清单文件的目录,加载被引用的文件,检查哈希值以确保文件完整性。发现类型的成员,执行继续 - 不同文件,不同程序集
如果引用的类型在其他程序集的文件中,“运行时”会加载被引用程序集的清单文件。如果需要的类型不在该文件中,就继续加载包含了类型的文件。发现类型的成员,执行继续。
- 在上例中,CLR发现System.Console在和调用者不同的程序集中实现。所以,CLR必须查找哪个程序集,加载包含程序集清单的PE文件。然后扫描清单,判断是哪个PE文件实现了类型。如果被引用的类型就在清单文件中,一切都很简单。如果类型在程序集的另一个文件中,CLR必须加载那个文件,并扫描其元数据来定位类型。然后CLR创建它的内部数据结构来表示类型,JIT编译器完成Main方法的编译。