本书是一本讲解.NET技术的书籍,目标读者群也是在.NET框架(.NET Framework)下进行开发的程序员,所以咱们没法回避的问题就是:什么是.NET框架?它包含了哪些内容?为开发程序提供了哪些支持?不少朋友对这类个问题的第一反应多是.NET框架所提供的庞大类库及编写代码所采用的C#语言,实际上远不止这些。html
要描述.NET框架,天然会遇到与其相关的一系列专业的技术术语和缩写,相信你们已经见到过许多了,好比:CLI、CIL、CTS、CLS、CLR、JIT、BCL、FCL、Module、Assembly 等,足以让不少人一头雾水、望而却步。笔者不会像字典同样按首字母排序对术语进行逐一解释,由于这样仍是难以理解。咱们仍是从你们最熟悉的东西开始吧!程序员
设想一下:编写下面这样一个最简单的显示“Hello, World!”的控制台程序,并将该程序运行起来须要哪几个步骤呢?编程
using System;
class Program {
static void Main(string[] args) {
string text = "hello, world!";
Console.WriteLine(text);
}
}浏览器
这些步骤包括:打开Visual Studio,建立一个C#控制台应用程序项目(在这里将它命名为ConsoleApp),编写代码,编译程序而后运行。虽然这样的程序谁都会写,可是再多进行一下思考就会发现,尽管是一个很小的程序,但已经引入了.NET框架的几个重要方面。缓存
若是建立一个VB.NET类型的项目,实现和上面C#项目彻底同样的功能,那么编译后生成的文件有什么区别?sass
编写控制台应用程序,将字符输出到屏幕,须要调用Console.WriteLine()方法。这个Console类型从何而来呢?安全
生成的文件在系统中是如何运行起来的?其机制和使用传统VC++生成的可执行文件是否相同?服务器
其实,上面每个问题的答案都包含.NET框架所提供的支持,这里将它分为三个部分:网络
接下来就针对上述内容开始为你们详细讲述。数据结构
首先要了解的就是C#程序源码在编译以后会获得什么样的一个文件。你们知道,过去使用VC++生成的可执行文件,通过预编译、编译、汇编、连接几个步骤后,最终生成的可执行文件中就已经包含了处理器的本地代码(Native Code),支持它运行的只是操做系统和本地的机器指令集。那么采用C#编译器生成的文件又是什么呢?如今须要引入程序集这个概念:在.NET框架下,相似C#这样的高级语言通过编译后生成的结果文件被称作程序集,其后缀名是.dll(类库)或.exe(可执行程序)。在引入这个概念以前,前面(上一节)提到程序集时,都是用“文件”这个词来描述的。
程序集的定义只是给编译后生成的文件一个稍微正式一点的名称,对于解释“它是由什么构成的”这个问题并无太大的帮助。为了进一步了解程序集,咱们再来作一个试验,使用VB.NET建立一个控制台应用程序项目(ConsoleAppVB),并生成一个程序集,代码功能和上面用C#建立的项目是同样的的。
Module Program
Sub Main()
Dim text AsString = "hello, world !"
Console.WriteLine(text)
EndSub
EndModule
如今,须要一个工具来查看这个程序集的内容,而且与C#项目生成的程序集进行对比。还好,微软已经提供了一个利器——IL DASM(IL Disassembler,IL反汇编程序)来帮助开发者查看程序集的信息。若是安装了Visual Studio,IL DASM将会随同Visual Studio一块儿安装。依次选择开始菜单→ Microsoft Visual Studio 2010 → Microsoft Windows SDK Tools →IL 反汇编程序(IL DASM)能够启动IL DASM。
打开IL DASM后选择VB.NET项目生成的ConsoleAppVB.exe,能够看到如图6-1所示的界面。
图6-1 IL DASM 运行界面
这部份内容不少,会在下一章“程序集”中进行专门讲述,,这里暂且略过。展开图6-1中的ConsoleAppVB.Program类型,在Main()方法上双击,会弹出另一个窗口,显示图6-2中的代码,看上去有点像汇编语言。在这里能够看到熟悉的string text变量声明及“hello, world !”。
图6-2 方法体的CIL语言描述(VB.NET)
接下来再打开C#项目生成的ConsoleApp.exe,进行一样的操做,在打开Main()方法后会发现其中的代码与图6-2中几乎彻底同样,如图6-3所示
图6-3方法体的CIL语言描述(C#)
至此,能够获得一个初步的推断:无论是VB.NET仍是是C#,编译以后的程序集都可以用IL DASM打开,所以它们生成的程序集的格式都是相同的;当程序所实现的功能相同时,程序集所包含的CIL代码也是相似的。
如今对上面程序集中所包含的相似汇编的语言作一下介绍,便是本节标题中的CIL(Common Intermediate Language,公共中间语言)。CIL最初是随着.NET由微软一块儿发布的,所以以前也叫作MSIL(Microsoft Intermediate Language),后来进行了标准化,以后便被称作CIL。在一些书或文章中,CIL也会简写为IL,其实都是指一样的东西。为了不混淆,本书统一用CIL这个缩写。
咱们能够将上面的过程用图6-4来表示出来。
图6-4 源程序编译为了程序集
接下来再深刻地分析一下,公共中间语言这个术语到底包含了哪几层含义。
为了加深一下印象,咱们来作一个试验:编写一段简单的CIL代码,而且使用IL ASM工具对其进行编译,获得和前面同样的ConsoleApp.exe程序。
1)打开记事本程序,输入下面的代码,而后将其保存在D:\ConsoleApp.il。
.assembly extern mscorlib{}
.assembly ConsoleApp{}
.module ConsoleApp.exe
.class public auto ansi Program extends System.Object
{
.method public static void Main()
{
.entrypoint
nop
ldstr "Hello, World!"
call void [mscorlib]System.Console::WriteLine(string)
nop
ret
}
}
2)打开Visual Studio 2010命令行工具,输入:
D:\>ilasm ConsoleApp.il
3)成功后会看到ConsoleApp.exe程序,它的执行结果和上面用C#编写的彻底同样。
因为程序集是由CIL语言所描述的,所以CIL也叫作程序集语言(Assembly Language)。又由于.NET程序集须要由.NET运行时加载才能运行,能够视其为由.NET运行时进行管理的,因此CIL代码也叫作托管代码(Managed Code)。相对的,不须要.NET运行时就能够执行的代码就叫作非托管代码(Unmanaged Code)。
好了,已经知道了CIL的存在,从如今开始,最好在头脑里创建起两个模型或两种视角:一种是基于C#或其余高级语言的源程序的视角,一种是基于CIL中间语言的程序集视角。C#源程序在被编译为程序集之后,就独立于C#,所以程序集能够由其余种类的语言所调用;同时,由于程序集并无包含本地机器的指令,因此它与具体的机器类型也分隔开了,能够被装有.NET框架的任何机器运行。
咱们先来看一个有意思的现象:再次打开前面建立的C#控制台项目(ConsoleApp),而后在解决方案面板下打开“引用”文件夹,若是用的是Visual Studio 2010,而且面向的目标框架是.NET 4.0版本,那么将会看到如图6-5所示的这些引用。
图6-5 解决方案中的“引用”文件夹
在建立项目时并无作任何额外的操做,那么这些引用显然是在建立项目时自动添加的。为了方便初学者,这里稍微解释一下:要使用(实际上笔者以为Consume这个词表达的更贴切)其余开发者所设计的类型,就须要在项目中将该类型所在的程序集引用进来。如今看到的这些程序集引用,都是微软认为很经常使用的,几乎是每一个项目都会使用到的,因此在建立项目时自动添加了进来,省得开发者再手动进行添加。
可是在这里这些引用不利于咱们理解一些内容,因此咱们把这些引用所有删除掉,如图6-6所示,而后再次编译程序。
图6-6 删除掉全部的项目引用
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApp {
classProgram {
staticvoid Main(string[] args) {
string text = "Hello, world!";
Console.WriteLine(text);
}
}
}
可能有人会认为,在删掉这些引用以后,编译器将会绝不客气地提示编译错误:未能找到类型或命名空间“System”(是否缺乏using指令或程序集引用?)。可实际上,当编译并运行上面的代码时,程序会正确无误地执行。这是由于咱们已经删掉了全部引用的程序集,只定义了一个Program类型,并无定义Console类型,因此此时要面对的第一个问题就是:Console类型从哪里来?
Visual Studio提供了一个快捷的办法使咱们能够快速查看类型:将光标定位在Console上,而后按下键盘上的F12,就能够看到Console的类型定义。在Console类型定义的最上方,能够看到它所在的程序集地址:C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll。
#region 程序集 mscorlib.dll, v4.0.30319
// C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll
#endregion
using System.IO;
using System.Runtime.ConstrainedExecution;
using System.Security;
using System.Text;
namespace System {
public static class Console {
// 中间略
}
}
能够看到Console类型来自于mscorlib.dll这个程序集。从上面的实验能够看出,无论咱们是否引用mscorlib.dll程序集,它老是会自动引用进来。这个程序集中所包含的类库,便是本节标题中的BCL(Base Class Library,基类库)。从名字就能够看出来,这个类库包含的都是些最基本的类型,其自己已经与CIL语言融为一提了,为CIL语言提供基础的编程支持,以致于该类库已经成为了CLI标准的一部分(后面会介绍,所以也能够说BCL中的类型就是CIL语言的类型,全部面向CIL的语言都可以使用它们。咱们可使用对象浏览器(Visual Studio菜单→视图→对象浏览器)来查看mscorlib.dll程序集中都包含了哪些命名空间和类型,如图6-7所示。
图6-7 mscorlib.dll中包含的命名空间
能够看到该程序集下包含的主要是System命名空间,稍微细心一点的读者会发现,在新建项目的时候,还包含了System.dll程序集,而且其中所包含的类型与mscorlib中的类型十分类似。
图6-8 System 程序集
图6-9 System.dll中包含的命名空间
这又是怎么回事呢?实际上,只要点开System命名空间就会发现,mscorlib.dll的System命名空间下面定义的类型和System.dll的System命名空间下面定义的类型彻底不一样,它们之间并无冲突之处。
如今就明白了:BCL提供了像Console这样的类型来支持开发者编写相似控制台这样的程序。
既然已经思考了这么多,不妨再深刻一下,思考这样一个问题:写下的这条语句string text = “hello, world !”,其中的string从哪里来?从直觉来看,string在Visual Studio中以深蓝色呈现,属于C#的关键字,那么它应该是C#提供的内置类型。但是,当咱们将光标移动到string上并按下F12时,转到string的定义时,看到的倒是下面这样的内容:
#region 程序集 mscorlib.dll, v4.0.30319
// C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll
#endregion
using System.Collections;
using System.Collections.Generic;
// 为了节约篇幅,省略了一些using
namespace System {
public sealed class String : IComparable, ICloneable, IConvertible, IComparable<string>, IEnumerable<char>, IEnumerable, IEquatable<string> {
// 省略定义
}
}
注意最上方的程序集地址,再次看到了mscorlib.dll,而且String类型与Console类型同样,同位于System命名空间下。因而可知,C#的关键字string,不过是BCL中System.String类型的一个别名而已。相似地,VB.NET中的String关键字也是BCL中的System.String类型的别名。所以,在.NET框架中,语言从本质上来讲没有太大的区别,更多的区别是在语法方面。从上面的例子也能够看出,C#和VB.NET的不少语言能力并非本身的,而是从CIL“借”过来的这样作也保证了在不一样语言中相应类型的行为是一致的。
表6-1列出了几个典型的,不一样语言关键字与CIL类型的对应关系。笔者以为理解重于记忆,因此这里只列出了几个。要了解其余基础类型时,只要将光标移动到类型上,而后再按下F12键就能够了。
表6-1不一样语言关键字与CIL类型的对应关系
CIL 类型 | C# 关键字 | VB.NET关键字 |
System.Byte | byte | Byte |
Sytem.Int16 | short | Short |
System.Int64 | long | Long |
从表6-1能够看出,.NET同时也对语言开发者提供支持.如你须要设计一款语言,那么在开发编译器时将语言的关键字映射为CIL中的类型就能够了,也就是说,对本身语言中的一些特殊符号(关键字)进行映射处理,就好像C#中的关键字int和string同样。
你们可能据说过这样一种特殊的类型——基元类型(Primitive Type)。实际上,讲到这里你们应该已经明白了,那些由编译器直接支持,将语言自己的关键字类型转换为CIL类型的,就叫作基元类型。显然,上面的byte、int、string都是基元类型。而C#中并无一个关键字去映射Console,因此咱们认为Console只是普通的类类型(Class Type)。
做为一名.NET程序员,天天都要打交道的就是FCL了(Framework Class Library,框架类库)。在上一节中介绍了BCL,它是FCL的一个子集。BCL中包含了与编译器及CIL语言关系紧密的核心类型,以及常见开发任务中都会使用到的类型。而FCL包含的内容极多,仅服务于一种应用场景的子类库就足够写一本书了,这里仅简单对它进行介绍。
从功能上来看,能够将FCL框架类库划分红如下几层。
假设要开发一套新的语言,这种语言和C#或VB.NET同样,在编译后也可以生成CIL代码,也能够在.NET环境下运行,那么首先须要什么呢?
根据6.2节所讲述的内容咱们知道,要开发的新语言至关于CIL的高级语言版本,因此实际上要作什么并非由新语言决定的,而是由CIL来决定的。所以,须要一套CIL的定义、规则或标准。这套规则定义了咱们的语言能够作什么,不能够作什么,具备哪些特性。这套规则就称做CTS(Common Type System,公共类型系统)。任何知足了这套规则的高级语言就能够称为面向.NET框架的语言。C#和VB.NET不过是微软本身开发的一套符合了CTS的语言,实际上还有不少的组织或团体,也开发出了这样的语言,好比Delphi.Net、FORTRAN等。
那么CTS具体包括哪些内容呢?在回答这个问题以前咱们须要弄清楚一个概念。仍是经过一段C#代码来讲明,先看下面几行代码:
public class Book {
// 省略实现
}
Book item1 = new Book();
Book item2 = new Book();
对于以上代码,一般是这么描述的:定义了一个Book类,而且建立了两个Book类的实例item一、item2。实际上这只包含了两层含义如表6-2所示。
表6-2 类、类的实例
类 | Book |
类的实例 | item1,item2 |
再思考一下就会发现,还有一个更高的层面,那就是Book这个类的类型,咱们称之为类类型(Class Type),所以上表能够改为如表6-3所示。
表6-3 类类型、类、类的实例
类类型 | class |
类 | Book |
类的实例 | item1,item2 |
相似的,还有枚举类型(Enum Type)、结构类型((Struct Type)等。如今你们应该明白这里要表达的意思了,CTS规定了能够在语言中定义诸如类、结构、委托等类型,这些规则定义了语言中更高层次的内容。所以,在C#这个具体的语言实现中,咱们才能够去定义类类型(Class Type)或者结构类型(Struct Type)等。
一样,能够在Book类中定义一个字段name并提供一个方法ShowName()。实际上,这些也是CTS定义的,它规范了类型中能够包含字段(filed)、属性(property)、方法(method)、事件(event)等。
除了定义各类类型外,CTS还规定了各类访问性,好比Private、Public、Family(C#中为Protected)、Assembly(C#中为internal)、Family and assembly(C#中没有提供实现)、Family or assembly(C#中为protected internal)。
CTS还定义了一些约束,例如,全部类型都隐式地继承自System.Object类型,全部类型都只能继承自一个基类。从CTS的名称和公共类型系统能够看出,不只C#语言要知足这些约束,全部面向.NET的语言都须要知足这些约束。众所周知,传统C++是能够继承自多个基类的。为了让熟悉C++语言的开发者也能在.NET框架上开发应用程序,微软推出了面向.NET的C++/CLI语言(也叫托管C++),它就是符合CTS的C++改版语言,为了知足CTS规范,它被限制为了只能继承自一个基类。
关于上面内容有两点须要特别说明:
1)C#并无提供Family and assembly的实现,C#中也没有全局方法(Global Method)。换言之,C#只实现了CTS 的一部分功能。,也就是说,CTS规范了语言可以实现的全部能力,可是符合CTS规范的具体语言实现不必定要实现CTS规范所定义的所有功能。
2)C++/CLI又被约束为只能继承自一个基类,换言之,C++中的部分功能被删除了。,就是说,任何语言要符合CTS,其中与CTS不兼容的部分功能都要被舍弃。
显然,因为CIL是.NET运行时所能理解的语言,所以它实现了CTS的所有功能。虽然它是一种低级语言,可是实际上,它所具备的功能更加完整。C#语言和CIL的关系,能够用图6-10进行表示。
图6-10 C#和CIL的关系
既然已经理解了CTS是一套语言的规则定义,就能够开发一套语言来符合CTS了。假设这个语言叫作N#,它所实现的CTS很是有限,仅实现了其中不多的一部分功能,它与CTS和C#语言的关系可能如图6-11所示。
图6-11 C#、N#和CIL的关系
那么如今就有一个问题:由C#编写的程序集,可以引用由N#编写的程序集吗?答案显然是不能,,虽然C#和N#同属于CTS旗下,可是它们并无共通之处。所以,虽然单独的N#或C#程序能够完美地在.NET框架下运行,可是它们之间却没法相互引用。若是使用N#开发项目的开发者原本就不但愿其余语言类型的项目来引用他的项目倒也罢了,可是,若是N#项目指望其余语言类型的项目可以对它进行引用,就须要N#中公开的类型和功能知足C#语言的特性,即它们须要有共通之处。注意,这句话中有一个词很重要,就是“公开的”(public)。N#中不公开的部分(private、internal、protected)是不受影响的,可使用独有的语言特性,由于这些不公开的部分原本就不容许外部进行访问。所以, 若是N#想要被C#所理解和引用,它公开的部分就要知足C#的一些规范,此时,它与CTS和C#语言的关系就会变成如图6-12所示。
图6-12 C#、N#、CIL的关系
若是世界上仅有C#和N#两种语言就好办了,把它们共同的语言特性提取出来,而后要求全部公开的类型都知足这些语言特性,这样C#和N#程序集就能够相互引用了。可问题是:语言类型有上百种之多,而且.NET的设计目标是实现一个开放的平台,不只现有的语言通过简单修改就能够运行在.NET框架上,后续开发的新语言也能够,而新语言此时并不存在,如何提取出它的语言特性?所以又须要一套规范和标准来定义一些常见的、大多数语言都共有的语言特性。对于将来的新语言,只要它公开的部分可以知足这些规范,就可以被其余语言的程序集所使用。这个规范就叫作CLS (Common Language Specification,公共语言规范)。很明显,CLS是CTS的一个子集。如今引入了CLS,图6-12的关系图就能够改为如图6-13所示。
图6-13 语言、CLS、CIL的关系
若是利用C#开发的一个程序集的公开部分仅采用了CLS中的特性,那么这个程序集就叫作CLS兼容程序集(CLScompliant assembly)。显然,对于上面提到的FCL框架类库,其中的类型都符合CLS,仅有极个别类型的成员不符合CLS,这就保证了全部面向.NET的语言均可以使用框架类库中的类型。
如今,读者又会有一个疑问:上面几段文字中反复出现了一个词———“语言特性”(language features),知足CLS就是要求语言特性要一致,那么什么叫作语言特性?这里给出几个具体的语言特性:是否区分大小写,标识符的命名规则如何,可使用的基本类型有哪些,构造函数的调用方式(是否会调用基类构造函数),支持的访问修饰符等。
那么咱们如何检验程序集是否符合CLS呢?.NET为咱们提供了一个特性CLSCompliant,便于在编译时检查程序集是否符合CLS。咱们来看下面一个例子:
using System;
[assembly:CLSCompliant(true)]
public class CLSTest {
public string name;
// 警告:仅大小写不一样的标识符“CLSTest.Name()”不符合 CLS
public string Name() {
return "";
}
// 警告:“CLSTest.GetValue()”的返回类型不符合 CLS
public uint GetValue() {
return 0;
}
// 警告: 参数类型“sbyte”不符合 CLS
public void SetValue(sbyte a) { }
// 警告标识符“CLSTest._aFiled”不符合 CLS
public string _MyProperty { get; set; }
}
能够注意到,在CLSTest类的前面为程序集加上了一个CLSCompliant特性,代表这个程序集是CLS兼容的。可是,有三处并不知足这个要求,所以编译器给出了警告信息。这三处是:
还会注意到,编译器给出的只是警告信息,而非错误信息,所以能够无视编译器的警告,不过这个程序集只能由其余C#语言编写的程序集所使用。
前面提到过:程序集包含了CIL语言代码,而CIL语言代码是没法直接运行的,须要通过.NET运行时进行即时编译才能转换为计算机能够直接执行的机器指令。那么这个过程是如何进行的呢?
接下来咱们要了解的就是.NET框架的核心部分:CLR(Common Language Runtime),公共语言运行时),有时也会称作.NET运行时(.NET runtime)。在了解CLR以前,须要先进一步学习一下程序集,由于下一节会对程序集进行专门的讲述,这里仅简单介绍一下程序集中对于理解CLR有帮助的概念。
从直觉上来看,前面以.exe为后缀的控制台应用程序就是一个直接的可执行文件,由于在双击它后,它确实会运行起来。这里的状况和面向对象中的继承有一点像:一台轿车首先是一部机动车、一只猫首先是一个动物,而一个.NET程序集首先是一个Windows可执行程序。
那么什么样格式的文件才是一个Windows可执行文件?这个格式被称作PE/COFF(Microsoft Windows Portable Executable/Common Object File Format),Windows可移植可执行/通用对象文件格式。Windows操做系统可以加载并运行.dll和.exe是由于它可以理解PE/COFF文件的格式。显然,全部在Windows操做系统上运行的程序都须要符合这个格式,固然也包括.NET程序集在内。在这一级,程序的控制权还属于操做系统,PE/COFF头包含了供操做系统查看和利用的信息。此时,程序集能够表示成如图6-14所示。
图6-14 程序集结构1
在前面提到过,程序集中包含的CIL语言代码并非计算机能够直接执行的,还须要进行即时编译,那么在对CIL语言代码进行编译前,须要先将编译的环境运行起来,所以PE/COFF头以后的就是CLR头了。CLR头最重要的做用之一就是告诉操做系统这个PE/COFF文件是一个.NET程序集,区别于其余类型的可执行程序。
图6-15 程序集结构2
在CLR头以后就是你们相对熟悉一些的内容了。首先,程序集包含一个清单(manifest),这个清单至关于一个目录,描述了程序集自己的信息,例如程序集标识(名称、版本、文化)、程序集包含的资源(Resources)、组成程序集的文件等。
图6-16 程序集结构3
清单以后就是元数据了。若是说清单描述了程序集自身的信息,那么元数据则描述了程序集所包含的内容。这些内容包括:程序集包含的模块(会在第7章介绍)、类型、类型的成员、类型和类型成员的可见性等。注意,元数据并不包含类型的实现,有点相似于C++中的.h头文件。在.NET中,查看元数据的过程就叫作反射(Reflection)。
图6-17 程序集结构4
接下来就是已经转换为CIL的程序代码了,也就是元数据中类型的实现,包括方法体、字段等,相似于C++中的.cpp文件。
图6-18 程序集结构
注意,图6-18中还多添加了一个资源文件,例如.jpg图片。从这幅图能够看出,程序集是自解释型的(Self-Description),再也不须要任何额外的东西,例如注册表,就能够完整地知道程序集的一切信息。
至此对程序集的简单介绍就先到这里,接下来看一下程序集是如何被执行的。
如今已经了解过了程序集,而且知道程序集中包含的CIL代码并不能直接运行,还须要CLR的支持。归纳来讲,CLR是一个软件层或代理,它管理了.NET程序集的执行,主要包括:管理应用程序域、加载和运行程序集、安全检查、将CIL代码即时编译为机器代码、异常处理、对象析构和垃圾回收等。相对于编译时(Compile time),这些过程发生在程序运行的过程当中,所以,将这个软件层命名为了运行时,实际上它自己与时间是没有太大关系的。有一些朋友在初学.NET的时候,纠结在了Runtime这个词上,总觉得和时间有什么关系,老是不能很好地理解CLR。笔者认为重要的是理解CLR是作什么的,而不用过于关注它的名称。
实际上,CLR还有一种叫法,即VES(Virtual Execution System,虚拟执行系统)。从上一段的说明来看,这个命名应该更能描述CLR的做用,也不容易引发混淆,可是可能为了和CIL、CTS、CLS等术语保持一致性,最后将其命名为了CLR。在这里,咱们知道CLR不过是一个.NET程序集的运行环境而已,有点相似于Java虚拟机。VES这个术语来自于CLI,会在6.7节进行讲述。
能够用图6-19来描述CLR的主要做用。
图6-19 CLR的主要做用
前面已经概要地了解了CLR的做用,接下来开始更进一步的学习。首先遇到的问题就是:CLR以什么样的形式位于什么位置?
因为CLR自己用于管理托管代码,所以它是由非托管代码编写的,并非一个包含了托管代码的程序集,也不能使用IL DASM进行查看。它位于C:\%SystemRoot%\Microsoft.NET\Framework\版本号下,视安装的机器不一样有两个版本,一个是工做站版本的mscorwks.dll,一个是服务器版本的mscorsvr.dll。wks和svr分别表明work station和server。
接下来再看一下CLR是如何运行起来的。虽然从Windows Server 2003开始,.NET框架已经预装在操做系统中,可是它尚未集成为操做系统的一部分。当操做系统尝试打开一个托管程序集(.exe)时,它首先会检查PE头,根据PE头来建立合适的进程。
接下来会进一步检查是否存在CLR头,若是存在,就会当即载入MsCorEE.dll。这个库文件是.NET框架的核心组件之一,注意它也不是一个程序集。MsCorEE.dll位于C:\%SystemRoot%\System32\系统文件夹下全部安装了.NET框架的计算机都会有这个文件。你们可能注意到了,这个库安装在System32系统文件夹下,而没有像其余的核心组件或类库那样按照版本号存放在C:\%SystemRoot%\Microsoft.NET\Framework\文件夹下。这里又存在一个“鸡生蛋问题”:根据不一样的程序集信息会加载不一样版本的CLR,所以加载CLR的组件就应该只有一个,不能再根据CLR的版本去决定加载CLR的组件的版本。
MsCorEE.dll是一个很细的软件层。加载了MsCorEE.dll以后,会调用其中的_CorExeMain()函数,该函数会加载合适版本的CLR。在CLR运行以后,程序的执行权就交给了CLR。CLR会找到程序的入口点,一般是Main()方法,而后执行它。这里又包含了如下过程:
能够看出,采用这种架构的一个好处就是,.NET程序集能够运行在任何平台上,无论是Windows、UNIX,仍是其余操做系统,只要这个平台拥有针对于该操做系统的.NET框架就能够运行.NET程序集。
CLI是一个国际标准,由ECMA和ISO进行了标准化,全称为Common Language Infrastructure(公共语言基础)。它只是一个概念和汇总,实际上本章的每一小节都是这个标准的一部分。CLI包括:CIL、CTS、CLS、VES、元数据、基础框架。
看到这里不少人会感受到有点奇怪,为何CLI和.NET框架包含的内容如此雷同?它们之间是什么关系?简单来讲,CLI是一个标准,而.NET框架是这个标准的具体实现。在CLI中,并无CLR的概念,只有VES,而CLR就是.NET框架中VES的具体实现。既然CLI只是一个标准,而.NET框架是它在Windows平台上的具体实现,那么是否是就只有.NET框架这一个CLI的实现?显然不是,Mono Project就是CLI标准的另外一个实现。Mono Project的目标就是将.NET框架多平台化,使其能够运行在各类平台上,包括Mac OS、Linux等。
CLI的详细信息能够在这里查看:http://www.ecma-international.org/publications/standards/Ecma-335.htm,感兴趣的朋友能够将它的PDF标准文档下载下来看一下。
本章系统的学习地介绍了一下.NET框架的底层知识,几乎包含了常见的全部术语,例如程序集、CIL、CTS、CLS、CLR等,同时也介绍了它们之间是如何相互协做共同构建起整个.NET平台的。相信通过本章的学习,你们会对.NET框架有一个更好的全局性认识。
感谢阅读,但愿这篇文章能给你带来帮助。
出处:http://www.cnblogs.com/JimmyZhang/archive/2012/11/27/2790759.html