微软的.Net在2001年作为新事物出现,给我们带来了很多新的概念、技术和术语,在日常的开发中很少关注,要深入了解.NET就必须先了解这些术语的含义:
CLR,common language runtime。是.Net支持的开发语言所共用的运行时。这个运行时提供了一些核心的功能:内存管理、程序集加载、安全性、错误处理、线程同步等。
其实CLR并不知道拿进来运行的代码是用什么语言写的,因为它只接受编译好的Managed Module。
Compiling Source Code into Managed Modules
Managed Module是一个标准的Windows可移植可执行(portable executable,简称PE)文件。32位的是 PE32, 64位的是 PE32+。 PE32可以在32位和64位上运行,而PE32+只可以在64位上运行。
下图就是源代码编译的过程:
从图中可以看出,你可以使用如何支持CLR的语言开发程序,只要用相应的编译器就可以编译成Managed Module。
下面列出了一个Managed Module所包含的内容:
内容 | 描述 |
PE Header | 这个文件的类型:GUI、CUI还是DLL 一个时间戳:标记这个文件被编译生成的时间。 |
CLR Header | 这个文件依赖的CLR的版本, 这个文件的启动方法的 MethodDef 元数据信息, 文件元数据,资源文件,强命名等的地址和大小。 |
元数据(Matedata) | 每一个Managed module 都包含两个Matedata table: 一个描述了这个文件中定义了哪些类和类的成员, 另外一个描述了这个文件引用了哪些类和类的成员。 |
IL Code | 编译器生产的 IL 中间语言代码,在运行时,由CLR把 IL 翻译为机器码。 |
IL 代码也叫托管代码。 值得注意的是,Matedata 和 IL 是编译过程的共同产物,一定是编译在一个文件中,不会各自独立存在。
Combining Managed Modules into Assemblies
虽然我们的代码时被编译为Managed Module,但是我们实际接触的更多的是程序集(Assembly),是的,实际上CLR是加载Assembly,而不是加载Managed Module。
Assembly是一个逻辑的的分组,包含一个或多个的Manged Module或资源文件。而且Assembly是可以被复用的,安全性(Security)部署的,可被版本标示的最小单位。
从图上看出,Assembly中有一个Manifest,这其实是另外一组Matedata,描述了这个文件由哪些Managed Module组成、这个Assembly实现的对外公开的类型、以及嵌入在内的资源文件和数据文件。
在VS.NET中,编译器默认帮我们把一个Project中的所有类型都编译为一个Assembly,你也可以按照自己的想法组织Assembly的构成,比如把不常用的类编译为一个独立的Assembly,要做到这一点,你就要自己操作关于Assembly的命令行工具,比如AL.exe(Assembly Linker)。
Assembly中还包含了引用到哪些其他程序集的信息。这一点让Assembly成为了自解析的,使得.Net程序的部署比之前的 C/C++,COM+的unmanaged components程序方便的多,不需要注册了。
Loading the Common Language Runtime
要运行.Net 的程序,目标机器必须安装 .Net Framework,但是如何判断一台机器是否安装了.Net Framework 呢?
答:检查%SystemRoot%\System32 目录下是否有 MSCorEE.dll 文件。
如何判断一个机器上有安装.Net Framework 的具体哪个版本?
答:检查注册表 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP 中的子键。或者在.Net Framework SDK 中有一个命令行工具:CLRVer.exe也可以列出本机安装的所有CLR的版本。
Executing Your Assembly’s Code
通常情况下,我们用高级语言开发程序,然后用相应的编译器生成 IL ,IL 比机器语言的级别高一些。但是 IL 也可以用汇编来写,微软也提供了相关的工具:IL Assembler,ILAsm.exe;IL Disassembler,ILDasm.exe。
要真正运行一个method(), IL 必须被转化为 机器指令,这个工作是由 CRL's JIT (just-in-time) 编译器完成的。
如何保护你的 IL 代码,
第一个层次是使用Web Service这样的服务,客户永远不能得到你的dll
第二个层次是使用一些第三方的工具,把你的 程序集中的 IL 中的变量混乱化。
第三个层次是使用CLR’s interoperability,可以控制一些托管代码和非托管代码的交互。
The Native Code Generator Tool: NGen.exe
安装.Net Framework 时,NGen.exe 也会安装到你的机器上,这个工具主要用来把 IL 代码编译成本地机器码,由于使用这个工具编译后的代码不再需要CLR's JIT 在运行时编译,所以可以提升程序的性能,但同时这样做也带来一些不好的因素,比如说用NGen.exe编译的机器代码不能根据具体的CPU规格来优化程序,但是JIT编译器却可以。
所以对于NGen.exe的使用要特别的小心,其实对于服务器端程序来说,比如ASP.NET只是客户端的第一次访问比较慢一些,后续的访问的运行速度都是很快的。对于客户端的程序,用NGen.exe编译的代码可以提高启动速度,减少进程的working set。
The Framework Class Library (FCL)
FCL就是由一大堆 DLL 组成,这些DLL中定义了成千上万的类以及这些类提供的功能。微软也不断的提供额外的类供开发人员使用,比如说 DirectX SDK。
使用FCL中的 Assembly,我们可以开发例如以下的应用类型:
- Web Services:使用 ASP.NET XML Web Service 或 WCF 可以很容易从Internet上访问的调用方法。
- Web Forms Apps:ASP.NET Web Forms Application 或者 Web Site。
- Rich Windows GUI Apps:Windows Form,WPF。
- Rich Internet Applications(RIAs):基于Internet的 Silverlight 技术。
- Windows Console Apps:控制台程序。
- Windows Services:服务。
- Database Store Procedure:SQL Server,Oracle,DB2 都允许开发人员用.Net 开发存储过程。
- Component Library:对立的组件,被上面的Apps来引用。
The Common Type System(CTS)
通过上面的了解,你会发现CLR对于我们来说最重要的就是一大堆的类,这些类为我们的应用程序开发提供了便利。由于类型是CLR的根本,所以微软定义了一个规范Common Type System 来描述如何定义一个类以及如何定义它的行为。
CTS规定一个类可以包含0个或多个成员,类的成员可以有这几种:Field、Method、Property、Event。
CTS还规定了类成员可视性的规则和可视性修饰符:
- Private:只能被同一个类的其他成员访问。
- Family:在C#/C++中的关键字是 protected,只能被类的子类访问,不管这个子类和父类是否在一个Assembly中。
- Family and assembly:只能被在同一个Assembly的子类访问,在C# 和 VB.NET 中都没有这个关键字。
- Assembly:只能被在同一个Assembly的其他类型访问,在C#中使用关键字 internal。
- Family or assembly:子类或同一个Assembly的都可访问,在C#中使用关键字 protected internal
- Pubic:可被任何类访问。
CTS还规定了类的继承规则,虚方法规则,对象生命周期的规则,等等。虽然你不需要学习CTS的这些规则,但是他们已经体现在你使用的高级语言中了。
CTS还有一个最重要的规则,所有的类最终必须继承自System.Object。
The Common Language Specification(CLS)
由于每种语言都是实现了CLR中的一个集合,每种语言所包含的集合不尽相同,那要保证不用语言编译出来的程序的互通,就有了CLS的存在,它定义了一个最小的集合是每种语言都必须实现并遵守的。
如果你想保证你的程序是遵守CLS的,可以给程序集加上一个Attribute: [assembly: CLSCompliant(true)]
1 using System; 2 3 // Tell compiler to check for CLS compliance 4 [assembly: CLSCompliant(true)] 5 6 namespace SomeLibrary 7 { 8 // Warnings appear because the class is public 9 public sealed class SomeLibraryType 10 { 11 // Warning: Return type of 'SomeLibrary.SomeLibraryType.Abc()' 12 // is not CLS-compliant 13 public UInt32 Abc() { return 0; } 14 15 // Warning: Identifier 'SomeLibrary.SomeLibraryType.abc()' 16 // differing only in case is not CLS-compliant 17 public void abc() { } 18 19 // No warning: this method is private 20 private UInt32 ABC() { return 0; } 21 } 22 }
编译程序会得到两个警告,一个是因为不是所有的语言都支持 Unsigned Int 类型,另外一个是因为两个方法名 Abc() 和 abc() 只有大小写的区别,在VB.Net中是无法区分的。
但是如果把 SomeLibraryType 前面的 public 关键字去掉,这个两个警告就会消失,因为这个类会成为 internal 的,其他Assembly不能访问他的内部,而和它同一个Assembly的类肯定和它是用同一中语言编写的。
Interoperability with Unmanaged Code
托管代码可以通过 P/Invoke(Platform Invoke) 的方式来调用非托管DLL中的功能。
在和非托管代码的互通性方面,接触的比较少,以后有需要时再来学习。