我们已经知道,当我们运行程序时,CLR将加载并初始化它,然后CLR会读取该程序集的CLR表头来寻找标识应用程序入口点方法(Main)的MethodDefToken。根据MethodDef元数据表,CLR会定位到文件中该方法的IL代码所处的偏移,然后将其以JIT的方式编译为本地代码,同时完成代码的类型安全验证过程,最后执行编译后的本地代码。当CLR以JIT的方式编译该段代码时,它会检测到所有引用的类型和成员,并加载定义它们的程序集。要加载程序集,CLR必须首先找到它的位置。
在解析一个被引用的类型时,CLR可以在以下三个地方中的其一找到该类型:
1.同一个文件。对同一个文件中类型的访问在编译时就确定下来(有时称为早绑定)。CLR直接从该文件中加载被引用的类型。
2.不同的文件,相同的程序集。CLR首先确保被引用的文件在当前程序集清单中的FileRef表内。CLR然后会在加载程序集清单文件的目录中查找到被引用的文件。该文件被加载的同时,CLR会检查它的散列值以确保文件的完整性,之后便会找到相应类型的成员。
3.不同的文件,不同的程序集。当被引用的类型在一个不同的程序集文件中时,CLR会首先加载包含被引用程序集的清单所在的文件。如果该文件没有包含需要的类型,CLR则会根据此清单文件加载适当的文件。这样也会找到相应类型的成员。
所以,如果某个方法被实现在一个和调用者不同的程序集中时,CLR必须搜索其实现程序集,并加载包含程序集清单的PE文件。CLR然后会扫描清单以确定该PE文件实现了所需的类型。如果清单包含了被引用的类型,加载过程便告完成。但如果被引用的类型实现在程序集的另一个文件中,CLR会加载该文件,扫描其中的元数据并定位需要的类型。完成类型的加载后,CLR会创建一个内部的数据结构来表示该类型。JIT编译器接着将完成Main方法的编译,并开始执行Main方法。
流程图显示了CLR如何使用元数据来定位某个类型所在的程序集:
如果执行过程中有一步失败,则相应的异常将抛出。
参考:《Microsoft .NET框架程序设计》