『框架设计(第2版)CLR Via C#』学习笔记(001)——将源代码编译成托管模块
声明:本文是读书笔记,大部分内容是照书抄的,作为自己学习使用,(*^__^*) 嘻嘻……
以前在开发系统时,总是要决定使用什么编程语言。这是一个很难的任务,因为不同的语言具有不同的能力。但是自从.net出现以后,问题得到了解决,让我们先来看看下面这个图吧
公共语言运行库(Common Language Runtime,CLR)是一个可以由多种不同的编程语言使用的运行库。CLR的特性可由面向它的所有语言来使用。事实上,在运行时,CLR根本不关心开发人员用哪一种语言来写源代码。也就是说,我们在挑选编程语言时,应该根据自己所要表达的意图选择最容易的编程语言。
实际上我们可以把编译器当作一个语法检查器和“正确代码”分析器。Microsoft面向这个运行库已经创建了几种语言的编辑器,例如c++/CLI、C#、Visual Basic、JScript、J#以及一个“中间语言”(Intermediate Language,IL)汇编器。除了Microsoft外,还有一些公司或学校或者爱好者创建了自己的编译器,它们也能面向CLR生成代码。
本地代码编译器生成的是一种特定的CPU构架专用的代码。相反,所有CLR相容的编译器生成的都是“中间语言(Intermediate Language,IL)”代码。IL代码有事也被称之为托管代码。
除了生成IL,面向CLR的每个编译器还需要在每个托管模块中生成完整的元数据(metadata)。简言之,元数据(metadata)是一系列特殊的数据表,它们描述了模块中定义的内容,比如类型及其成员,除此之外,还由一些元数据表指出了托管模块引用的内容,比如导入的类型及其成员。虽然我们知道元数据是一些老技术的超集。这些老技术包括“类型库(Type Library)”和“接口定义语言(Interface Definition Language,IDL)”文件等。要注意的一个重点是,CLR的元数据比它们要完整的多。另外,和类型库及IDL不同,元数据总是与包含IL代码的文件关联在一起。事实上,元数据总是嵌入与代码相同的EXE/DLL,这使两者密不可分。由于编译器同时生成元数据和代码,并将它们绑定到最终生成的托管模块中,所以元数据及其描述的IL嗲吗永远不会失去同步。
那么托管模块究竟包含些什么那,让我们来看看:
- PE32或PE32+头 标准Windows PE文件头,它类似于“公共对象文件格式(Common Object File Format,COFF)”头。 如果这个头使用PE32格式,则文件能在Windows的32位或64位版本上运行。如果这个头文件使用PE32+格式,则文件只能在Windows64位版本上运行。这个头还标示了文件类型:GUI、CUI或者DLL,并包含一个时间戳来指出文件的生成时间。对于只包含IL代码的模块,PE32(+)头的大多信息会被忽视。对于包含本地CPU代码的模块,这个头包含了与本地CPU代码有关的信息。
- CLR头 包含使这个模块成为一个托管模块的信息(由CLR和实用程序来解释)。头中包含所要求的CLR版本,一些标志,托管模块入口方法(Main方法)的MethodDef元数据标记,以及模块的元数据、资源、强名称、一些标志及其他不太重要的数据项的位置/大小。
- 元数据 每个托管模块都包含元数据表。主要由两种类型的表:一种类型的表描述源代码中定义的类型和成员,另一种类型的表描述源代码中引用的类型和成员。
- 中间语言(IL)代码 编译器编译源代码时生成的代码。在运行时,CLR将IL编译成本地CPU指令。
下面让我们来简单的看一下元数据到底由什么用途,这里仅列出一部分。
- 在编译时,元数据消除了对头和库文件的需求,因为与引用的类型/成员有关的所有信息都包含在用IL来实现类型/成员的文件中。编译器可以直接从托管模块读取元数据。
- Microsoft Visual Studio使用元数据来帮助我们写代码。它的“智能感知(IntelliSense)”特性能解析元数据,指出一个类型提供了什么方法、属性、事件和字段。如果是一个方法,还能指出方法需要什么参数。
- CLR的代码验证过程使用元数据来确保代码只执行“安全”的操作。
- 元数据允许将一个对象的字段序列化到一个内存中,将其发送给另一台机器,然后反序列化,在远程机器上重建对象状态。
- 元数据允许垃圾收集器跟踪对象的生存期。垃圾收集器能判断任何对象的类型,并根据元数据知道那个对象中的哪些字段引用了其他对象。