Site hosted by Angelfire.com: Build your free website today!
Google
About US
Computer Programming
Favorites

Home >> Columns >> Effective C# >>

1. 正确区分类集(Assembly)、模块(Module)和名称空间(Namespace)

陈铭                   Microsoft .NET MVP
  .NET是面向网络应用和服务的开发平台,那么就让我们从一个简单的网络应用开始吧——虽然这并
不是本章节要讨论的主题。
	//hello.cs: Say Hello to the Internet
	using System;
	using System.Net;
	
	namespace Effective.Csharp.Chapter1 {
		public class HelloInternet {
		   public static void Main() {
		      string address = "http://www.google.com/search?q=";
		      string hello = "Hello, Internet";
			WebRequest myReq = WebRequest.Create(address + hello);
			WebResponse myResponse = myReq.GetResponse();
			myResponse.Close();
		   }
		}
	}

  在向互联网问好之前,我们先得编译我们的程序,因为程序里引用了System.Net,所以编译命令似乎应
该这样:
csc /r:System.Net.Dll hello.cs

  但是编译器很快就抱怨起来:
error CS0006: Metadata file 'System.Net.Dll' could not be found

  看来编译器没有找到System.Net.Dll文件,但怎么会呢?类库参考里分明写着WebRequest和
WebResponse是在System.Net名称空间里的啊!在解决这个问题之前,我们不妨先看看下面几个概念
的定义:

  名称空间(Namespace):C++和Java程序员对名称空间的概念应该并不陌生,名称空间用于在逻辑上将
一组功能相关的类型组织在一起,从而避免简单类型命名引起的冲突。比如,一个生物学的类库中可能
会包含“树”的类型定义,而描述计算机数据结构的应用中同样需要对“树”结构进行定义。在一个没有名
称空间概念的系统中,由于命名的冲突,我们将不能在同一个应用中同时使用上述两个类型库。而在
.NET中利用名称空间解决这个问题轻而易举:

namespace Biology {
        public class Tree {
            //生物学中对“树”的描述
        }
}

namespace DataStructure { 
        public class Tree {
            //计算机数据结构中对“树”的描述
        }
}

  这样,只要在程序中使用“名称空间.类型名”的方式引用类型,就可以完全避免歧义:
//生物学“树”的实例
Biology.Tree tree1 = new Biology.Tree(“Pine”);            
//数据结构的“树”实例
DataStructure.Tree tree2 = new DataStructure.Tree(); 

  之所以说名称空间只是在“逻辑上”组织类型,是因为它仅仅在设计开发阶段供程序员和编译器将不同的
类型区分开来。运行系统并不理会名称空间的存在,而只是简单的把名称空间看成类型名的一个部分。
系统中没有与名称空间对应的目录或文件,同一个名称空间下的类型编译之后也不一定会在物理上存储
在一起。
  C#允许使用using语句简化名称空间的引用,using 在功能上更接近于C++的using namespace语句,
而不是Java的import。它仅仅为程序员在代码中引用类型提供一种简略形式,而不是为编译器提供任
何类型信息的实际存储位置,比如:
System.Console.WriteLine(“Hello”); 
 
  在没有命名冲突的情况下完全等价于:
using System;
…
Console.WriteLine(“Hello”);

  即使是存在命名冲突的情况下,使用using语句也可以极大的简化冗长的名称空间的引用。例如,
在System.Windows.Forms和System.Web.UI.Controls两个名称空间中都存在名称为Button的类,
如果在同一个项目中我们必须同时使用这两个类(事实上通常情况下很少会在同时引用这两个类,
这里仅为说明问题),可以这样写:
using WinForm=System.Windows.Forms;
using WebForm=System.Web.UI.Controls;

WinForm.Button winbtn = new WinForm.Button();
WebForm.Button webbtn = new WebForm.Button();

  那么既然名称空间不能提供实际类型信息存储的位置,编译器是如何取得这些类型信息从而完成编译
的呢?.NET又是如何在物理上把各种类型组织在一起的呢?为此,.NET引进了类集(Assembly)概念。

  类集(Assembly):类集是.NET中相关类型的物理组织形式。它是进行应用程序部署、版本控制、重
用和权限分配的基本单位。类集包含了类集清单(Manifest)、在类集中定义的类型信息(Metadata)、
类方法的实现代码以及其他资源。类集可以由一个或多个文件组成,这些文件或者包含资源数据,或
者是包含类型信息和实现的PE格式文件。
  包含了类型信息和实现的PE格式文件称为一个模块(Module),模块是运行时类型加载的基本单位。如
果程序引用了模块中的某个类,那么CLR将会把整个模块加载到内存中。关于如何有效分割模块提高程
序性能的更多信息,请参考条款X。

  类集清单(Manifest)包含了类集层次上的信息元数据,例如类集的名称、版本以及它所包含的文件等。
类集清单必须完整的保存在类集的一个模块当中,如下图所示:

  

  在编译C#程序的时候,编译器要求必须指明在哪些类集中查找当前程序引用的类型,例如:
System.Console类的实现包含在mscorlib类集中,那么编译一个引用了System.Console的程序的
编译命令为:
csc /r:mscorlib.dll myprog.cs
  其中,/r:引用的mscorlib.dll是mscorlib类集中包含类集清单的模块。(事实上对于mscorlib类
集并不需要特别引用,稍候将会介绍)

  类集与名称空间的关系
  .NET文档中对两者关系的描述是:“名称空间在概念上与类集成正交关系”。用更通俗的话说,它们之间
没有任何直接联系。一个名称空间中的类可能分布在几个类集当中,一个类集也可能包含几个不同的名称
空间中类的实现。系统类库中确实存在很多同名的类集和名称空间,但那只能说是命名上的巧合罢了。
下表列出了常用的名称空间以及它们的实现类集之间的对应关系:
名称空间对应的类集
Systemmscorlib, System
System.IO mscorlib, System
System.Xml System.Data, System.Xml
System.Data System.Data
System.Net System
System.Reflection mscorlib,
System.Security mscorlib,
System.InteropServices mscorlib,
System.Runtime.Remoting mscorlib,
System.Runtime.Serialization mscorlib,
  这里我们看到System.Net名称空间的实现包含在System类集中,所以要正确编译本章开始提及的程序,
我们必须在编译命令中引用System类集而不是System.Net:
csc /r:System.Dll hello.cs
程序顺利编译通过。

  

  似乎故事至此应该告一段落了,但是——稍等片刻:既然我们必须向编译器指出程序中需要引用的类集,
为什么编译一个引用了System.Console类的程序并不真的需要加上/r:mscorlib.dll?为什么有时候
引用了System.Xml名称空间的类型却并不需要加上/r:System.Data.Dll或者/r:System.Xml.Dll呢?

  这里有两种不同的情形:mscorlib类集在.NET类库中的地位非常特殊,所有的内建类型都定义在这个
类集中,而且.NET类继承体系的根类Sytem.Object也定义在这个类集当中。几乎所有的应用程序都必须
引用这个类集,所以C#编译器在编译过程中会自动加上对mscorlib类集的引用。
另外,C#编译器允许使用配置文件来简化编译参数的设置,例如将如下内容保存到C#配置文件MyExe.rsp:
   #注释:编译source1.cs source2.cs,生成MyExe.exe
/target:exe /out:MyExe.exe source1.cs source2.cs
然后就可以通过指定配置文件的方式来编译MyExe程序了:
csc @MyExe.rsp

  需要引用的类集也是配置文件的内容之一,可以在配置文件中指定程序需要引用的类集。鉴于一些类集在编
写程序的过程中比较常用,C#的设计者将这些类集的引用放在了C#编译器的缺省配置文件当中,该配置文
件位于%SystemRoot%\Microsoft.Net\Framework\%version%\csc.rsp,其中%SystemRoot%是
Windows的安装目录,而%version%是.NET Framework的版本号。该文件的内容如下:
# This file contains command-line options that the C#
# command line compiler (CSC) will process as part
# of every compilation, unless the "/noconfig" option
# is specified. 

# Reference the common Framework libraries
/r:Accessibility.dll
/r:Microsoft.Vsa.dll
/r:System.Configuration.Install.dll
/r:System.Data.dll
/r:System.Design.dll
/r:System.DirectoryServices.dll
/r:System.dll
/r:System.Drawing.Design.dll
/r:System.Drawing.dll
/r:System.EnterpriseServices.dll
/r:System.Management.dll
/r:System.Messaging.dll
/r:System.Runtime.Remoting.dll
/r:System.Runtime.Serialization.Formatters.Soap.dll
/r:System.Security.dll
/r:System.ServiceProcess.dll
/r:System.Web.dll
/r:System.Web.RegularExpressions.dll
/r:System.Web.Services.dll
/r:System.Windows.Forms.Dll
/r:System.XML.dll
  这样,C#编译器在编译任何程序的时候,都会自动引用以上列出的所有类集——除非通过使用/noconfig显式
指定忽略配置文件。
2002 Copyright by Ming Chen & Qiu Zhang
and contributors who are credited in the articles.