第22章 命名空间和程序集
本章将更加深入地学习命名空间(Namespace)和程序集(Assembly)的相关知识。学习完本章,读者应可以掌握命名空间的正确用法。
另外,本章使用了较大的篇幅深入介绍了程序集的相关知识,包括程序集的结构、程序集清单等。我们还将学习应用程序域(AppDomain)和全局程序集缓存(GAC)的知识,并将学习如何在运行时动态创建程序集并动态加载。使用这部分动态创建程序集的知识,甚至可以为自己喜欢的语言编写一个.NET编译器。
22.1 命名空间
命名空间是一种将类型(类、结构、接口等)组织在一起的机制,并以此限制类型的应用范围,以避免与其他类型发生名称冲突。很多语言都有“命名空间”这一概念或者类似概念,除了C#外,C++、Java甚至XML都有命名空间的概念。其中,Java中的类似的概念是“包(package)”,本质上和“命名空间”是相同的。
在使用Visual Studio 2010创建程序时,IDE将自动创建一个和项目名称相同的命名空间。若要在程序中使用其他命名空间的类,必须使用using指令来引用相应的命名空间。
“命名空间”的作用主要是:从逻辑层次上组织代码中众多的类、接口等类型。要特别强调的是辑层次,因为不同的命名空间可能位于一个程序集中。程序集提供的是物理层次的组织方式。例如,常用的Console类,以及所有类的基类Object,它们都定义于System命名空间。
下表列出了最常用的一些命名空间,如表22-1所示。
接下来将介绍如何使用命名空间,包括定义和引用命名空间。
22.1.1 使用命名空间
我们知道,BCL是一个庞大的类库,如果没有命名空间机制,所有的类型都放在一起,就好像把家里所有的东西全部都堆放在客厅,那将是什么样的结果?可想而知!因此,BCL是由命名空间组成,每个命名空间由类、结构、接口、委托、事件、枚举等类型组成。可见,有了命名空间,就可以根据各类型的功能和作用进行分门别类。
命名空间不止是BCL这样的大型类库需要,我们自己的编程项目也同样需要。要使用命名空间非常简单,其实到目前为止,我们提供的很多示例代码都使用了ProgrammingCSharp4命名空间,如下所示:
namespace ProgrammingCSharp4
{
class NamespaceSample
{
}
};
上述代码中,就定义了一个命名空间ProgrammingCSharp4,因此定义命名空间的语法如下:
namespace CSharpIntroduction
{
……
}
可见,使用namespace关键字可以定义一个命名空间。如果使用Visual Studio 2010编写代码,在新建的类中,IDE会帮我们定义一个默认的命名空间,命名空间的名称为当前程序集名,如图22-1所示。
图22-1中,程序集的名称为ProgrammingCSharp4,因此I D E自动为我们定义了这样一个命名空间,中间的NamespaceSample类就是我们新建的类名,然后就可以在此代码框架上进行更多的定义。
namespace关键字后紧跟的是命名空间名称,当然需要使用空格隔开。该命名可以是一个有效的标识符;也可以使用多个标识符,但中间要使用"."分隔,这样就定义一个嵌套的命名空间,使用"A.B.C"就定义3个嵌套的命名空间,这种方式避免了逐个去定义每个命名空间,节省了代码,也更加清晰简洁,代码清单22-1更好地说明了这一点。
图 22-1 命名空间的组成
代码清单22-1 嵌套的命名空间
namespace ProgrammingCSharp4.Samples
{
class NamespaceSample
{
}
}
代码清单22-1 和代码清单22-2是完全等价的,但很显然前者要简洁得多。
代码清单22-2 另一种形式的嵌套命名空间
namespace ProgrammingCSharp4
{
namespace Samples
{
class NamespaceSample
{
}
}
}
“命名空间名+类型名”这一整体有个名字,叫做“完全限定名”。顾名思义,使用该名称可以完全“限定”一个唯一的类,换句话说,可以保证此类型名的唯一性。使用“完全限定名”可以在没有引用此类型所在的命名空间的情况下也可以对类型进行访问。例如:System.Console以及ProgrammingCSharp4.HelloWorldClass都是完全限定名,前者可以在不引用System命名空间时访问Console类型,而后者可以在不引用ProgrammingCSharp4命名空间时访问HelloWorldClass类型(如果访问此类型的类也在同一个命名空间,则不需要再引用ProgrammingCSharp4命名空间)。
上述示例代码在编译后,通过ILDasm查看,如图22-2所示。
图 22-2 通过ILDasm查看ProgrammingCSharp4.exe程序集中的NamespaceSample类
学习了命名空间的定义,接下来学习如何引用命名空间。引用了其他命名空间或者类型后,就可以直接使用类型名而不必再使用完全限定名。我们知道,大多数C#应用程序都是从一个using指令节开始的,该节列出应用程序将会使用的命名空间,其中不但包括当前程序集中的命名空间,也包括其他程序集中的命名空间。引用了命名空间后,就可以避免使用类型的完全限定名了。回顾一下第3章的"HelloWorld"示例程序,如代码清单22-3所示。
代码清单22-3 HelloWorld示例程序
可见,虽然Console类定义在System命名空间中,由于使用using关键字引用了System命名空间,由此避免了使用Console类的“完全限定名”——System.Console。从这个简单的例子可以看出,命名空间使得代码更有条理性,也更简洁。更多的好处还需要读者在使用过程中慢慢体会。
关于命名空间,还有一些重要的信息需要做个补充说明。注意,某些信息是隐含的,如下:
❑命名空间的访问级别默认是隐式public,且不能使用任何的访问级别修饰符,即使public也不行;
❑一个命名空间的定义要么是顶级的,要么是作为其他命名空间的成员,在代码清单22-2中,命名空间ProgrammingCSharp4是顶级的,而命名空间ProgrammingCSharp4.Samples则是前者的成员;
❑无论是顶级还是作为其他命名空间的成员,所有命名空间的名称都必须在包含它的命名空间中保持唯一。例如对于命名空间A.B来说,A中已经定义了命名空间B了,就不能再定义一个B命名空间。当然,如果再定义一个顶级的命名空间B(和A同级)是没有问题的。