cuclife.com > IT > C# > 0

CLR via C# 1.1 将源代码编译为托管模块

网络整理 - 06-27

CLR顾名思义,是指可被各种不同编程语言使用的运行时。CLR的核心特性可用于所有面向它的编程语言。例如,运行时使用异常来报告错误,那么所有面向运行时的语言都可以通过异常来得到错误报告。又如,运行时也允许创建线程,因此所有面向运行时的语言也都可以创建线程。

在运行时,CLR对开发人员使用什么语言来完成源代码一无所知。我们可以选择任意语言来编写代码,只要其编译器可以编译面向CLR的代码。

如何选择语言呢?作者认为编译器是语法检查器和“正确代码”的分析器,它检查源代码,确保你编写的有意义,并输出描述你意图的代码。不同的语言拥有不同的语法。针对不同的目的,可以选择不同的语言。可以使代码量大幅缩减。

微软目前创建的面向CLR的语言编译器有:C++/CLI、C#、Visual Basic、F#、IronPython、IronRuby和一个中间语言汇编器(Intermediate Language Assembler)。

托管模块

下图显示了编译源代码文件的过程。如图所示,我们可以使用任何支持CLR的语言来编写源代码文件,然后相应的编译器将检查语法,并分析源代码。不管使用何种编译器,最终的结果都是托管模块(Managed Module)。托管模块是一个需要CLR才能够执行的标准的32位Microsoft Windows可移植可执行(PE32)文件,或标准的64位Windows可移植可执行(PE32+)文件。

托管模块的组成

  • PE32 or PE32+ header:标准PE文件头,类似于Common Object File Format头。如果头使用PE32格式,文件可以运行在32位或64位的Windows当中。如果头使用PE32+格式,文件必须运行在64位Windows当中。该头还指名了文件的类型:GUI、CUI或DLL,同时还包含了一个用来指明文件何时创建的时间戳。对于只包含IL代码的模块,PE32(+)中的大量信息都会被忽略。对于包含本地CPU代码的模块,头还包括本地CPU代码的信息。
  • CLR header:包含标识托管模块的一些信息(可以被CLR或一些工具解析)。包括托管模块所需要的CLR版本号、一些标记、托管模块入口点方法(main方法)的MethodDef元数据标记、以及有关托管模块的元数据(metadata)、资源(resources)、强命名(strong name)、标记(flags)、和其他一些意义不大的信息的位置和大小。
  • Metadata:每个托管模块都包含一些元数据表。元数据表主要分两种:一种用来描述代码中定义的类型和成员。一种用来描述代码中引用的类型和成员。
  • IL code:编译器在编译源代码时产生的代码。在运行时,CLR将IL编译为本地CPU指令。
  • 大多数早期编译器产生的代码都是面向特定CPU架构的。而所有CLR兼容的编译器所生成的都是IL代码。IL代码也被称为托管代码(managed code),因为CLR管理其执行。

    元数据

    除了生成IL,每个面向CLR的编译器还为需要所有托管模块生成完整的元数据。简而言之,元数据是一个数据表的集合,这些数据表有一些描述托管模块所定义的内容(如定义的类型和它们的成员),此外还有一些描述托管模块所引用的内容(如引用的类型和它们的成员)。元数据是一些早期技术如COM类型库和IDL(接口定义语言)文件的超集。需要指出的是,CLR的元数据远比它们要完整。并且与类型库和IDL不同的是,元数据总是与包含IL代码的文件相关联。事实上元数据总是和这些代码(IL代码?)一起嵌入于同一个EXE/DLL文件中,两者不可分离。由于编译器同时产生元数据和IL代码,并将它们绑定到生成的托管模块之中,因此元数据及其所描述的IL代码之间总能保持同步。

    元数据的用途

  • 元数据使得编译时不再需要本地C/C++头文件和库文件,这是因为在含有实现类型和成员的IL代码的文件中,已经包含了被引用的类型和成员的信息。编译器可以直接从托管模块中读取元数据。
  • Visual Studio使用元数据来帮助我们编写代码。智能感知特性通过解析元数据来告诉我们一个类型所提供的方法、属性、事件和字段,以及方法中的参数。
  • CLR的代码验证过程使用元数据来确保代码仅执行“类型安全”的操作。
  • 使用元数据可以将一个对象的字段序列化到内存块中,传递到另外一台计算机中,然后反序列化,并在远程计算机上重新创建对象的状态。
  • GC使用元数据跟踪对象的生命周期。对于任何对象,GC能够从元数据中获得其类型,以及哪个字段引用了其他的对象。
  • 有关元数据更详细的内容,在第2章。

    C#、VB.NET、F#和IL编译器总是产生包含托管代码(IL)和托管数据(GC数据类型)的模块。最终用户要执行这些包含托管代码和托管数据的模块,就必须安装CLR(目前作为.NET Framework的一部分发布)。这和要运行MFC或VB6应用一定要安装MFC库和VB DLLs是同样的道理。

    默认情况下,C++编译器生成的是包含非托管(本地)代码的EXE/DLL模块,在运行时操作的是非托管数据(本地内存)。执行这些模块不需要CLR。然而,如果指定/CLR命令行选项,C++编译器将生成包含托管代码的模块,并且执行环境也必须安装CLR。C++是唯一允许开发者编写托管和非托管代码并将其生成到同一个模块中的编译器。这使得开发者可以在非托管代码中使用托管代码。

    CLR via C# 1.1 将源代码编译为托管模块