公共语言运行库(Common Language Runtime CLR)这一名称准确描述了它的作用--它是一个可由多种不同编程语言使用的运行库。CLR的特性可由面向它的所有语言使用。
事实上,在运行时,CLR根本不关心开发人员用哪种语言来写源代码。这意味着在挑选编程语言时,应该选择最容易表示自己意图的语言。可以使用任何编程语言来开发代码,只要用于编译的代码编译器是面向CLR的。
本地代码编译器生成的是一种特定的CPU构架(比如x86,x64或者IA64)专用的代码。相反,所有CLR相容的编译器生成的都是“中间语言(Intermediate Language,IL)”代码。IL代码也称托管代码,因为CLR会管理它的执行。
除了生成IL,面向CLR的每个编译器还需要在每个托管模块中生成完整的元数据。简而言之,元数据(metadata)是一系列特殊的数据表,它们描述了模块中定义的内容,比如类型及其成员。除此之外,还有一些元数据表指出了托管模块引用的内容,比如导入的类型及其成员。元数据是一些老技术的超集。这些老技术包括“类型库(Type Library)”和“接口定义语言(Interface Definition Language,IDL)”文件等。要注意的一个重点在于,CLR元数据比它们完整的多。另外,和类型库及IDL不同,元数据总是包含IL代码的文件关联在一起。事实上,元数据总是嵌入与代码相同的EXE/DLL,这使两者密不可分。由于编译器同时生成元数据和代码,并将他们绑定到最终生成的托管模块中,所以元数据及其描述的IL代码永远不会失去同步。
元数据的用途有:
1.在编译时,元数据消除了对头和库文件的需求,因为与引用的类型/成员有关的所有信息都包含在用IL来实现类型/成员的文件中。编译器可直接从托管模块读取元数据。
2.CLR的代码验证过程使用元数据来确保代码只执行“安全”的操作
3.元数据允许将一个对象的字段序列化到一个内存块中,将其发送给另一台机器,然后反序列化,在远程机器上重建对象状态。
4.元数据允许垃圾收集器跟踪对象的生存期。垃圾收集器能判断任何对象的类型,并根据元数据知道哪个对象中的哪些字段引用了其他对象。
Microsoft的C#,Visual Basic,JScript,J#和IL汇编器总是生成包含托管代码(IL)和托管数据(垃圾回收的数据类型)的模块。最终用户只有在其机器上安装好CLR,才能执行包含托管代码以及/或者托管数据的模块。这类似于为了运行MFC或Visual Basic6应用程序,用户必须安装Microsoft Foundation Class(MFC)库或者Visual Basic DLL。
默认情况下,Microsoft的C++编译器将生成包含非托管代码和非托管数据的EXE/DLL模块。这些模块不要求CLR来执行。然而,指定一个CLR命令行开关,C++编译器也能生成包含托管代码的模块。当然,用户必须安装CLR才能执行这种代码。在Microsoft编译器中,最特殊的就是C++编译器,因为只有它才允许开发人员同时写托管和非托管代码,并生成到同一模块中。它也是允许开发人员在源代码中同时定义托管和非托管数据类型的惟一一种Microsoft编译器。Microsoft C++编译器的灵活性是其他编译器望尘莫及的,因为它允许开发人员在托管代码中使用他们现有的本地C/C++代码,并在逐渐习惯之后开始使用托管类型。
CLR实际并不和模块一起工作。相反,它是和程序集一起工作的。程序集(Assembly)是一个抽象的概念,初学者往往很难把握它的精髓。首先,程序集是一个或多个模块/资源文件的逻辑性分组。其次,程序集是最小的重用,安全性以及版本控制单元。取决于编译器或工具作出的选择,既可以生成单文件程序集,也可以生成多文件程序集。在CLR的世界中,我们将程序集称为“组件(component)”。
默认情况下,将生成的托管模块转换成一个程序集的实际工作由编译器来完成。换言之,c#编译器生成含有一个清单的托管模块。这个清单指出程序集只有一个文件构成。所以,假如项目只有一个托管模块,同时没有资源(或数据)的文件,那么程序集就是一个托管模块。在生成过程中,不需要执行额外的步骤。但是,假如希望将一系列文件合并到一个程序集中,就必须使用更多的工具。
程序集将一个可重用的,可保护的,可版本控制的组件的逻辑及物理表示区分开。具体如何将代码和资源划分到不同的文件中,完全取决于个人。例如:可以将很少用到的类型或资源放到一些单独的文件中,并将这些文件作为程序集的一部分。这些单独的文件可以根据需要从网上下载。如果文件永远用不上,则永远不会下载。这样不仅节省磁盘空间,还缩短了安装时间。利用程序集,可以分别在不同的地方部署文件,同时仍然将所有文件作为一个整体来看待。
程序集的模块还包含与引用的程序集相关的信息(包括它们的版本号)。这些信息使程序集具有“自描述(self-describing)”性。换言之,CLR能判断出为了执行程序集中的代码,程序集的直接依赖对象是什么。不需要在注册表或Microsoft Active Directory目录服务中保存额外的信息。由于无需额外信息,所以相较于非托管组件,程序集部署起来容易的多。