10.1 引用其他程序集
编译器接受源代码文件并生成名称为程序集的输出文件。
在许多项目中,你会想使用来自其他程序集的类或类型。这些其他的程序集可能来自BCL,或来自一个第三方卖主,或你自己创建了它们。这些程序集称为类库。
程序集文件的名称通常以.dll扩展名结尾而不是.exe扩展名。
要使用其它程序集的类,需要给编译器一个到该程序集的引用,给出它的名称和位置。
在Visual Studio中,可以用下面的方法把引用添加到项目:
- 选择Solution Explorer并在该项目名下找到References目录。References目录包含项目使用的程序集的列表。
- 右键点击References目录并选择Add Reference。有五个可以从中选择的标签页,允许你以不同的方法找到类库。
- 选中后,点击OK按钮,引用就被加入到项目了。
mscorlib库
程序集mscorlib.dll含有C#类型以及大部分.NET语言的基本类型的定义。在编译C#程序时,它必须总是被引用,所以Visual Studio不把它显示在References目录中。
10.2 命名空间
命名空间把一组类型分组在一起并给它们一个名称,称为命名空间名称。命名空间名称应该描述命名空间的内容并和其他命名空间名称相区别。
namespace SimpleNamespace
{
TypeDeclarations;
}
注意命名空间名称的两个有趣的事情:
- 命名空间可以包含前缀。
- 公司名称在命名空间名称的开始。
带有命名空间名称和类名的整体字符串被称为完全限定名。
命名空间名称
关于命名空间名称的一些要求如下:
- 命名空产是名称可以是任何有效标识符。
- 命名空间名称可以包括句点符号,用于把类型组织成层次。
下面是建议的命名空间命名指南:
- 使用公司名开始命名空间名称。
- 在公司名之后跟着技术名称。
- 不要把命名空间命名为与类或类型相同的名称。
命名空间的补充
关于命名空间,有其他几个要点应该知道:
- 在命名空间内,每个类型名必须有别于所有其他类型。
- 命名空间内的类型称为命名空间的成员。
- 一个源文件可以包含任意数目的命名空间声明,可以顺序也可以嵌套。
命名空间跨文件伸展
命名空间不是封闭的。这意味着可以在该源文件的后面或另一个源文件中再次声明它,以对它增加更多的类型声明。
嵌套命名空间
一个命名空间可以是另一个命名空间的成员。这个成员被称为嵌套的命名空间。嵌套命名空间允许你创建类型的概念层次。
虽然嵌套的命名空间是外层命名空间的成员,但它的成员不是外层命名空间的成员。人们最初有对嵌套的命名空间的误解是既然嵌套的命名空间在外层命名空间内部,那么嵌套命名空间的成员必须是外层命名空间的子集。这是不对的,这些命名空间是分离的。
有两种方法声明一个嵌套的命名空间:
- 原文嵌套:可以把命名空间的声明放在一个封闭的命名空间声明体内部,从而创建一个嵌套的命名空间。
- 分离的声明:也可以为嵌套命名空间创建分离的声明,但必须在声明中使用它的完全限定名称。
10.3 using指令
完全限定名可能相当长,在代码中通篇使用它们会变得十分乏味。然而,有两个编译指令,可以使你避免不得不使用完全限定名:using命名空间指令和using别名指令。
关于using指令词的两个要点如下:
- 它们必须主入在源文件的顶端,在任何类型声明之前。
- 它们应用于当前源文件中的所有命名空间。
using命名空间指令
using命名空间指令通知编译器你将要使用来自某个指定命名空间的类型。然后你可以继续,并使用简单类名而不必全路径修饰它们。
当编译器遇到一个不在当前命名空间的名称时,它检查在using命名空间指令中给出的命名空间列表,并把该未知名称加到列表中的第一个命名空间后面。如果结果完全限定名称匹配了这个程序集或引用程序集中的一个类,编译器将使用那个类。如果不匹配,那么它试验列表中下一个命名空间。
using命名空间指令由关键字using跟着一个命名空间标识符组成。
using System;
using别名指令
using别名指令允许起一个别名给:
- 命名空间
- 命名空间内的一个类型
using Syst =System;
using SC=System。Console;
程序集不包含本地机器的代码,而是公共中间语言的代码。它还包含实时编译器(JIT)在运行时转换CIL到本机代码所需的一切,包括对它所引用的其他程序集的引用。程序集的文件扩展名通常为.exe或.dll。
大部分程序集由一个单独的文件构成。包含四个主要部分:
- 程序集的清单包含:
- 程序集名称标识符。
- 组成程序集的文件列表。
- 一个指标程序集中内容在哪里的地图。
- 关于引用的其他程序集的信息。
- 类型元数据部分包含该程序集中定义的所有类型的信息。这些信息包含关于每个类型要知道的所有事情。
- CIL部分包人启程序信要的所有中间代码。
- 资源部分是可选的,但可以包含图形和语言资源。
尽管大部分程序集由单文件组成,但有些也有多个文件。对于有多个模块的程序集,一个文件是主模块,而其他的是次要模块。
- 主模块含有程序集的清单和到次要模块的引用。
- 次要模块的文件名以扩展名.netmodule结尾。
- 多文件程序集被视为一个单一单元。它们一起部署并一起定版。
10.5 程序集标识符
在.NET框架中,程序集的文件名不像在其他操作系统和环境中那么重要,更重要的是程序集的标识符(identify)。
程序集的标识符有四个组成部分,它们一起唯一标识了该程序集,如下所示:
- 简单名:这只是不带文件扩展名的文件名。每个程序集都有一个简单名,它也被称为程序集名或友好名称。
- 版本号:它由四个句点分开的整数字符串组成,以MajorVersion.MinorVersion.Build.Revision的形式。
- 文化信息:它是一个字符 串,由2~5个字符组成,代表一种语言,或代表一种语言和一个国家或地址。例如,在美国使用英语的文化名是en-US。在德国使用德语,它是de-DE。
- 公钥:这个128字节字符串应该是生产该程序集的公司唯一的。
10.6 强命名程序集
强命名(strongly named)程序集有一个唯一的数字签名依附于它。强命名程序集比没有强名称的程序集更加安全,这是由于以下原因:
- 强名称唯一标识了程序集。没有其他人能创建一个与之有相同名称的程序集,所以用户可以确信该程序集来自于其声称的来源。
- 没有CLR安全组件来捕获更改,带强名称的程序集的内容不能被改变。
弱命名(weakly nameed)程序集是没有被强命名的程序集。由于弱命名程序没有数字签名,它天生是不安全的。因为一根链的强度只和它最弱的一环相同,所以强命名程序集默认只能访问其他强命名程序集。(还存在一种方法允许“部分地相信调用者”)。
程序员不产生强名称。编译器产生它,接受关于程序集的信息,并哈希这些信息以创建一个唯一的数据签名依附到该程序集。它在哈希处理中使用的信息如下:
- 组成程序集的字节序列。
- 简单名称
- 版本号
- 文化信息
- 公钥/私钥对
创建强命名程序集
要使用Visual Studio 2008强命名一个程序集,必须有一份公钥/私钥对文件的拷贝。如果没有密钥文件,可以让Visual Studio产生一个。可以实行以下步骤:
- 打开工程的属性。
- 选择签名页。
- 选择为程序集复选框签名并输入密钥文件的位置。
在编译代码时,编译器会生成一个强命名的程序集。
10.7 程序集的私有方式部署
在目标上部署一个程序就像在该机器上创建一个目录并把应用程序复制过去一样简单。如果应用程序不需要其他程序集(比如DLL),或如果所需的DLL在同一目录下,那么程序应该会就在它所在的地方良好工作。这种方法部署的程序集称为私有程序集,而且这种部署方法称为复制文件(XCopy)部署。
私有程序集几乎可以被放在任何目录中,而且只要它们依赖的文件都在同一目录或子目录下就足够了。事实上,可以在文件系统的不同部分有多个目录,每个目录都有同样的一组程序集,并且它们都会在它们各自不同的位置良好工作。
关于私有程序集部署的一些重要事情如下:
- 私有程序集所在的目录被称为应用程序目录。
- 私有程序集可以是强命名的也可以是弱命名的。
- 没有必要在注册表中注册组件。
- 要卸载一个私有程序集,只要从文件系统中删除它即可。
10.8 共享程序集和GAC
私有程序集是非常有用的,但有时你会把一个DLL放在一个中心位置,这样一个单独的拷贝就被系统中其他的程序集共享。.NET有这样的贮藏库,称为全局程序集缓存(GAC)。放进GAC的程序集称为共享程序集。
关于GAC的一些重要内容如下:
- 只有强命名程序集能被添加到GAC。
- 虽然GAC的早期版本只接受带.dll扩展名的文件,现在也可以添加带.exe扩展名的程序集了。
- GAC位置在名称为Assembly的子目录下,在Windows系统目录中。
把程序集安装到GAC
当试图安装一个程序集到GAC时,CLR的安全组件首先必须检验程序集上的数字签名是否有效。如果没有数据签名,或它是无效的,系统产针不会把它安装到GAC。
然而,这是个一次性检查。在程序集已经在GAC内之后,当它被一个正在运行的程序引用时,不再需要进一步的检查。
gacutil.exe命令行工具允许从GAC添加程序或删除程序集,并列出GAC包含的程序集。它的三个最有用的参数标记如下所示:
- /i:把一程序集插入GAC。
- /u:从GAC卸载一个程序集。
- /l:列出GAC中的程序集。
GAC内的并肩执行
程序集的标识符由完全限定名称的全部四个部分组成。所以,如果一个库的版本号改变了,或如果它用一个不同的公钥,这些区别指定了不同的程序集。
在GAC中可以有许多不同的程序集,它们有相同的文件名。虽然它们有相同的文件名,它们是不同的程序集而且在GAC中完美地共存。这使不同的应用程序在同一时间很容易使用不同版本的同一DLL,因为 它们是带不同的标识符的不同程序集。这被称为并肩执行(side-by-side Execution)。
10.9 配置文件
配置文件含有关于应用程序的信息,供CLR在运行时使用。它们可以指标CLR去做这样的事情,比如使用一个不同版本的DLL,或搜索程序引用的DLL时在附加目录中查找。
配置文件由XML代码组成,并不包含C#代码。它的用途是更新一个应用程序集以使用新版本的DLL。
10.10 延迟签名
公司小心地保护它们官方的公钥/私钥是非常重要的,否则, 如果不可靠的人得到了它,就可以发布伪装成该公司的代码。为这种情况,公司显然不能允许自由访问含有它们的公钥/私钥对的文件。在大公司中,最终程序集的强命名经常在开发过程的最尾部有特殊的有密钥访问权限的小组执行。
可是,由于个别原因,这会在开发和测试过程中导致问题。首先,由于公钥是程序集标识符的四个部分之一,所以直到提供了公钥它才能被设置。而且,弱命名的程序集不能被部署到GAC。开发人员和测试人员都需要有能力编译和测试该代码,并使用它将要被部署发布的方式,包括它的标识符和在GAC中的位置。
为了允许这个,有一种修改了的赋值强命名的形式,称为延迟签名(delayed signing)或部分签名(partial signing),它克服了这些问题,而且没有释放对私钥的访问。
在延迟签名中,编译器只使用公钥/私钥对中的公钥。然后公钥可以被放在完成的程序集的标识符清单中。延迟签名还使用一个为0的块保留数字签名的位置。
要创建一个延迟签名的程序集,必须做两件事情。第一,创建一个密钥文件的拷贝,它只有公钥而不是公钥/私钥对。下一步,为程序集范围内的源代码添加一个名称为DelaySignAttribute的附加特性,并把它的值设为true。
如果你试图部署延迟签名的程序集到GAC,CLR不会允许,因为它不是强命名的。要在这台机器上部署它,必须首先使用行指令取消在这台机器上的GAC签名确认,只针对这个程序集,并允许它被装在GAC中。要做到这点,从Visual Studio命令提示中执行下面的命令。
sn -vr MyAssembly.dll
强命名程序集、延迟签名程序集和强签名程序集的结构区别: