面试出现频率:虽然很重要但不怎么出现,可能会考你定义,以及程序集包括什么,然后自然的话题就跑到反射上去了。
重要程度:8/10,很重要
需要理解的程度:知道程序集包括IL和元数据。知道元数据的作用以及反射的概念。知道GAC是什么。关于反射在后面另有独立章节。对于程序集的强命名,个人认为过于偏僻。
3.1 概念
程序集构成了基于.NET的应用程序的部署、版本控制、重用和安全权限的基本单元。程序集以可执行 (.exe) 文件或动态链接库 (.dll) 文件的形式出现。它们向公共语言运行时提供了解类型实现所需要的信息。可以将程序集看成是构成逻辑功能单元并为一起工作而生成的类型和资源的集合。
如果程序集中含有多个命名空间,则每个命名空间有自己的IL和元数据(即托管模块)。多个托管模块合成一个程序集。CLR是和程序集一起工作的,而不是和托管模块一起。
如果你的程序只是Hello World级的小控制台应用程序,那么编译之后,可能你只会用到.NET最主要的基础类库mscorlib.dll(最重要的程序集之一)。但对于团队级的系统来说,可能会有大量dll文件作为类库。如果你在VS中选择新建一个Class Library,则编译后生成的结果文件是dll文件,没有可执行程序,你也不能在VS中试图运行一个Class Library。
不同程序集中相同的命名空间中相同的成员(例如类型)被认为是不同的。例如My.dll和Your.dll同时在一个命名空间A中定义了一个类B,则它们是不同的。
程序集是自描述的:它的清单部分含有它需要访问的其他程序集(依赖对象)名单,它的元数据包含了程序集中所有类型以及它们的成员。它的IL代码则包括了成员的实现。
程序集是可配置的:可以将其配置到私有或共享(全局程序集缓存,GAC)中。当你在一个类库中引用其他程序集(通过Add References)时,系统将该程序集的dll文件拷贝到你的类库的子目录bin\Debug下(这就是私有配置)。注意Add References不会显示GAC中的程序集。全局的程序集不需要Add References,IDE自动添加。配置到GAC的步骤是一个很偏僻的话题,可参考https://msdn.microsoft.com/zh-cn/library/yf1d93sz.aspx。
3.2 程序集的结构
程序集最重要的两部分是IL和元数据。它们合称托管模块。程序集包括以下部分:
- PE/COFF头:包含了供操作系统查看和利用的信息。Windows操作系统能够加载并运行.dll和.exe是因为它能够理解PE/COFF文件的格式。
- CLR头:告诉操作系统这个PE/COFF文件是一个.NET程序集,区别于其他类型的可执行程序。程序集中包含的IL语言代码并不是计算机可以直接执行的,还需要进行即时编译,那么在对IL语言代码进行编译前,需要先将编译的环境运行起来。
- 清单(manifest):相当于一个目录,描述了程序集本身的信息,例如程序集标识(名称、版本、文化)、程序集包含的资源(Resources)、组成程序集的文件、该程序集需要用到的所有外部程序集名单等。
- 元数据:如果说清单描述了程序集自身的信息,那么元数据则描述了程序集所包含的内容。这些内容包括:程序集包含的模块、类型、类型的成员、类型和类型成员的可见性等。注意,元数据并不包含类型的实现,有点类似于C++中的.h头文件。在.NET中,查看元数据的过程叫做反射(Reflection)。
- IL:也就是元数据中类型的实现,包括方法、属性等。
- 资源文件: 例如图标文件,文本文件,.resx资源文件等。
3.3 元数据的作用
部分元数据的作用:
- IDE通过元数据进行智能感知,例如在你打出一个.之后,自动弹出下拉菜单,获得类型的方法和属性等。
- CLR的代码验证过程使用元数据确保代码只执行类型安全的操作。
- 序列化和反序列化的基础。
- 通过访问元数据来获得类型的成员(即反射)。虽然这会降低性能,但很多时候必须要这么做,例如类型是动态类型,ORM框架即为一个常见的场景。
3.4 程序集和命名空间有何区别?
命名空间是一个程序集内相关类型的一个分组。例如System.IO命名空间包含了有关文件IO的类型。有时,多个程序文件可能共享一个命名空间。例如如果你开发一组几何类圆圈,三角和正方形,你可以将他们的命名空间都设为“Shapes”。
命名空间可以嵌套。例如namespace System.IO等同于
namespace System{
namespace IO{
…
}
}
一个程序集可以包括多个命名空间。在不同程序集中相同名字的命名空间是不同的两个对象。程序集和命名空间的主要区别:
- 程序集是部署,重用应用程序的最小单位,但命名空间不是,它更多的是将具有相似内容的一组类型和方法组织到一起。例如mscorlib.dll中的System命名空间,包含了.NET所有的基元类型。
- 一个程序集可以包括多个命名空间,反之则不行
- Using引用的对象是命名空间,而不能是程序集。你不能using mscorlib.dll。但当你using 例如System.Data(这是一个嵌套的命名空间)时,你可以使用System.Data命名空间的所有可访问类,属性及方法,就像其代码是你的一部分一样。
3.5 什么是GAC?
当你安装了CLR,你就有了一个Global Assembly Cache(全局程序集缓存,GAC)。安装CLR时,系统将把它认为重要的若干程序集放入GAC,例如mscorlib.dll。从 .NET Framework 4 开始,全局程序集缓存的默认位置为 %windir%\Microsoft.NET\assembly。 在 .NET Framework 的早期版本中,默认位置为 %windir%\assembly。
有时候当安装某些应用程序时,也会触发安装程序将程序集放入GAC。
GAC是一个机器级别的程序集,其中包括mscorlib.dll等至关重要的程序集。在Add Reference中,它不会被自动包括进来,必须手动浏览才可以找到部署到GAC中的程序集。如果你打算将类库部署到GAC,一般来说,这个库应当被大量其他工程引用。
不能把可执行的程序集部署到GAC。部署到GAC的细节,参阅精通C#第14章以及https://msdn.microsoft.com/zh-cn/library/yf1d93sz.aspx。在全局程序集缓存中部署的程序集必须具有强名称。将一个程序集添加到全局程序集缓存时,必须对构成该程序集的所有文件执行完整性检查。
4 综合问题
题目:hello world程序。
1 using System; 2 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 string text = "hello, world!"; 8 Console.WriteLine(text); 9 } 10 }