.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文档中对两者关系的描述是:“名称空间在概念上与类集成正交关系”。用更通俗的话说,它们之间
没有任何直接联系。一个名称空间中的类可能分布在几个类集当中,一个类集也可能包含几个不同的名称
空间中类的实现。系统类库中确实存在很多同名的类集和名称空间,但那只能说是命名上的巧合罢了。
下表列出了常用的名称空间以及它们的实现类集之间的对应关系:
名称空间 | 对应的类集 |
System | mscorlib, 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显式
指定忽略配置文件。
|