附录:这个是我读《 .NET本质论 》的读书笔记,其中会有大量引用原文的地方,我所做的就是摘取了其中的精华并做上标记,进行了自己觉得更合理的编排,希望大家喜欢。
CLR是一个更好的COM
组件技术主要强调独立开发和部署的程序之间的约定(contract)。
COM:组件对象模型(Component Object Mode)是Microsoft公司首次尝试将这些约定规范化。
CLR 公共语言运行库(Common Language Runtime )
为了处理COM约定及其定义所引发的问题,Microsoft公司的COM与MTS小组计划开发一个新的组件平台,名为COM3。就在选定该名称不久,Microsoft的多个组织一致认为,在特定的Microsoft平台下,COM3这个名称并不合适。随即将其更名为 公共对象运行库[Component Object Runtime(COR)]。在开发过程中,还采用过COM+在内的其他名称。很快地,又变为 通用运行库[Universal Runtime(URT)]。在第一个beta版发布之前,最终将名称定为 公共语言运行库[Common Language Runtime(CLR)]。
CLI 公共语言基础结构(Common Language Infrastructure )
CLI 包含了:
CTS 公共类型系统(Common Type System );
CIL 公共中间语言(Common Intermediate Language );
底层文件;
元数据格式。
CLR 是Microsoft所拥有并控制的CLI 的一个实现。
CLR约定与COM约定的二个差别:
1. 不同于COM,CLR自从问世以来,就有完全规范的格式描述组件之间的约定。这种格式一般称为元数据(metadata)。
CLR元数据是机器可读的(machine-readable),其格式是完全规范的。此外,CLR提供了一些实用部件,使得程序员在不懂底层文件格式的情形下,就能够读写元数据。通过定制特性(attribute;其本身就是强类型的),CLR元数据可以达到清晰容易的可扩展性。CLR元数据还包括组件的依赖关系和版本信息,这就允许使用一些新的技术来处理组件的版本控制问题。CLR元数据的存在是强制性的;你要部署或者加载组件,都必须访问元数据。
2. CLR约定与COM约定的第二个差别就是约定本身的特性。
在COM中,组件约定暗示了堆栈约定、虚函数表,以及作为方法参数传递的数据结构在内存中的表示形式。
在CLR中,约定被描述为类型的逻辑结构。特别是,CLR约定不会描述任何数据的内存表示形式。CLR推迟确定有关内存的表示形式,直到在运行时类型被首次加载。约定的虚拟化(virtualization)很大程度上降低COM二进制约定所带来的不稳定性,这是因为组件间不存在任何内存表示形式的假设。由于CLR类型定义是逻辑的,而不是物理的,因此,CLR的约定中并没有暗示访问字段或方法的精确的代码顺序。
实现数据表示形式和方法地址的虚拟化有一个重要前提条件。由于约定的物理方面(例如,方法表/字段的位移量)在组件编译时都不知道,因此,就需要引进某种机制,延期这些位移量的解析,直到代码实际部署,以便在特定处理器构架中生成组件的最终版本。为了实现这个可能性,CLR的组件中几乎不包含机器代码,准确地说,基于CLR组件采用的公共中间语言[Common Intermediate Language(CIL)]用于他们的实现。
CIL不只是一个处理器无关(processor-neutral)的指令集,它具有抽象能力,它将与机器代码密切关联的物理数据表示形式抽象出来。被CIL使用的操作码(opcode),在访问字段或调用方法时,不再使用绝对位移量或地址。准确地说,为了操作字段或方法,那些CIL会包含元数据的引用。这些引用只是基于字段或方法的名字与签名,而不是其位置或位移量。只要目标组件中存在与名字与签名匹配的字段或方法,CLR就没有必要选择物理位移量。
要注意,CLR不会直接执行CIL。准确地说,CIL在执行前总是被翻译为本机的机器语言。要么是在组件载入内存时,要么是组件被安装到部署机器上时,翻译就会被执行。无论哪种情况,当CIL到本机代码的翻译完成时,任何数据类型或方法的实际内存表示形式将被用于生成本机的机器代码,从而得到高效代码。
CIL生成的本机代码同样受益于高性能的物理耦合方式(physical coupling),这也是C++和COM所采用的方式。然而,C++和COM在形式阶段就考虑这种物理耦合,而CLR在CIL到本机代码翻译发生之前,不会解析这种物理绑定的细节。由于翻译是在部署机器上翻译的,因此,部件以外所需的类型定义将与部署机器上的某个类型相匹配,而不是开发者的机器上。这极大的减少了跨组件约定的不可靠性,同时,又不降低性能。
绝大多数用于CLR的新程序是通过更高级别的抽象编写的。CLR的编程思想就是一切都是类型、对象或者值。为此,CLR提供一套服务,统称为托管执行(managed execution)。在托管执行下,CLR是万能的,拥有运行程序各方面的完整信息。其中,包括方法中局部变量的状态和生存期的知识;还包括用于每一个堆栈帧(stack frame)的代码的开始位置;以及所有现存对象和对象引用的知识,包括可达性信息(reachability information)。
CLR程序员被鼓励放弃过去非托管的编程风格,特别是摒弃显式地内存管理,而采用分配和使用类型实例。同时,程序员还被鼓励放弃线程管理,而采用CLR工具,执行并发方法。
只有时间能说明CLR是不是“太过抽象”;不过,笔者坚信,迁移到托管执行环境[例如,CLR或者Java虚拟机(JVM)],这是一个进步,而不是倒退。一直以来,当程序员面临生产率(productivity)和可控性(control)的选择时,随着时间的推移,能够让他们拥有更高生产率的技术更可能胜出。图1.1展示了这两个因素之间的关系。
新编程模型的另一个关键方面是对元数据的很强的依赖性。并且,元数据对于运行在CLR内部或外部的程序是可访问的,这独立于元数据本身被用来将CIL翻译为本机代码这个事实。因此,运行在CLR内部或外部的任何程序也都能够访问元数据。
CLR也通过名为CodeDOM的工具支持生成编码。CodeDOM允许C#、VB.NET和JavaScript程序在内存中构造,并以强类型对象模型,而不是文本流。CodeDOM支持代码的内存中编译,并且,容许针对常用的高级编程语言,引入新的生成技术(包括新的编程语言编译器),而不是CIL与元数据特性的低层方面。
CLR是加载和执行代码的平台。
一般情况下,只要编译器能够发射CLR元数据和CIL的编程语言,CLR都可以支持。编程语言犹如冰淇淋的口味儿,各人有不同的喜好。
尽管C#自身的语法大量地借鉴了C、C++以及Java的特点,但是,C#终究是另一种编程语言,它有自己的一套基于CLR的约定和构造。表1.1列出了.NET Framework软件开发工具包[software development kit(SDK)]1.0版明确支持的5种编程语言的比较情况。
特征 |
VB.NET |
JScript |
C# |
C++ |
ILASM |
编译器 |
VBC.EXE |
JSC.EXE |
CSC.EXE |
CL.EXE |
ILASM.EXE |
CodeDOM支持 |
是 |
是 |
是 |
否 |
是 |
动态地添加成员 |
否 |
是 |
否 |
否 |
否 |
迟绑定 |
自动 |
自动 |
手工 |
手工 |
手工 |
自定义值类型 |
是 |
否 |
是 |
是 |
是 |
大小写敏感 |
否 |
是 |
是 |
是 |
是 |
无符号整型 |
否 |
是 |
是 |
是 |
是 |
方法重载 |
是 |
是 |
是 |
是 |
是 |
操作符重载 |
否 |
否 |
是 |
是 |
N/A |
C风格指针 |
否 |
否 |
是 |
是 |
是 |
本机/非托管指针 |
否 |
否 |
否 |
是 |
否 |
代码验证 |
总是 |
总是 |
可选 |
从不 |
可选 |
不透明的/非托管类型 |
否 |
否 |
否 |
是 |
是 |
模板/泛型 |
否 |
否 |
否 |
是 |
否 |
多重继承 |
否 |
否 |
否 |
是 |
否 |
CLR是组件软件的一个发展阶段,像其前身COM一样。
CLR支持基于强类型约定的组件的集成,这些约定都是基于逻辑结构的,这意味着其中没有低层物理数据的表示形式。
虚拟化让我们朝着纯粹语义约定迈进了一步。组件约定是通过CLR元数据描述的。
CLR元数据是一种可扩展的、机器可读的交换格式,它在基于CLR的程序和架构中随处可见。