1 命名空间
1.1 简介
在本章中,我们将详细的了解命名空间及其要求。还将学习创建和使用自己的命名空间。同时我们将学习.NET的基类库(BCL)中一些重要的类。随后我们将使用BCL中的命名空间System.Threading在程序中实现多线程。
1.2 命名空间
在开发大型项目时,会创建许多类,有时这些类的名称可能会冲突。有两种方法可以解决这个问题:第一种方法是将这些类重命名,使其名称前缀为描述性的唯一标记,这样就不会再发生名称冲突,但是这种做法会导致出现更多不必要的、难于记忆的名称。第二种方法就是使用命名空间。
除了可以避免命名冲突外,命名空间还专用于组织代码。如果想在其他某个应用程序中重用代码,通过在这些代码中使用命名空间,可以降低工作的复杂性。
命名空间在C#中是不可缺少的。在前面讨论的示例中使用了命名空间System。前面提到过,命名空间System中包含与系统交互所需的全部代码,这里所说的系统包括控制台(屏幕)和键盘。
1.2.1 声明命名空间
声明命名空间与声明类的方式相同。声明命名空间的语法如下。
Namespace NameSpaceName
{
//此处列出此命名空间的所有类!
}
声明命名空间的过程可以解释为下列步骤:
指定关键字namespace,并在其后加上命名空间的名称
左大括号
声明命名空间成员
右大括号
我们来看看下面的代码,以便更好地理解此概念。
代码段1:
class SamsungTelevison
{
…
}
class SamsungWalkMan
{
…
}
class SonyTelevison
{
…
}
class SonyWalkMan
{
…
}
在上面的代码段1 中,以两家公司Sony和Samsung为例。这两家公司都同信电视机(Television)和随身听(WalkMan)。但却不能有两个名称相同的类。因此,必须在类名称前加上公司名做为前缀。这样才好区分两家公司的产品。
代码段2:
namespace Samsung
{
class Television
{
…
}
class WalkMan
{
…
}
}
namespace Sony
{
class Television
{
…
}
class WalkMan
{
…
}
}
这种处理命名冲突的方法无疑更简洁,更有条理,使结构更清晰。而命名空间还可以进行嵌套。
1.2.2 嵌套命名空间
我们看看示例就非常清楚什么叫嵌套命名空间了。
代码段3:
…
namespace Sony
{
namespace Television
{
class T14inches
{
…
}
class T21inches
{
…
}
}
}
…
我们再看看另一种创建嵌套命名空间的方法。
代码段 4:
…
namespace Sony.Television
{
class T14inches
{
…
}
class T21inches
{
…
}
}
…
1.2.3 访问修饰符和命名空间
命名空间是隐式公共的。不能通过指定访问修饰符来重写其访问属性。不能将命名空间设置为受保护的、私有的或内部的,因此它们只能作为公共的存在。
1.2.4 限定命名
在类所属的命名空间内使用该类时,只需指定类名,这称为“非限定名称”(也称为“简称”),相反我们称之为“限定命名”。
1.3 using 命名空间指令
如果我们要引入的是嵌套的命名空间,那么我们可以简单的使using指令。
Using Sony.Television;
T14inches Television=new T14inches();
1.3.1 using 命名空间指令的作用域
如果using命名空间指令在所有成员声明之前被声明,则它拥有全局作用域,如果using命名空间指令在命名空间内声明,其作用域随着命名空间而结束。
1.3.2 二义性名称
以代码段2为例。Television类同时存在于命名空间Sony和Samsung中。会有什么结果?
using Sony;
using Samsung;
class Test
{
static void Main()
{
Television MyEntertainment = new Television();
}
}
这将会产生一个编译时错误。这种情况就只有使用限定的命名空间。
1.4 using别名指令
我们看个例子就明白了
using T21inches = Sony.Televisions.T21inches; //别名T21inches
class Test
{
static void Main()
{
T21inches M=new T21inches();
}
}
请注意,到目前为止所讨论的代码段中,都是在命名空间中使用类。然后,还可以在命名空间中很好的使用结构、接口、枚举和委托。
1.5 .NET基类库
.NET基类库BCL是用于创建应用程序的最全面的库之一。BCL由大量预先编写的代码组成。可以很容易地将这些代码合并到应用程序中,并在应用程序中加以使用。BCL中包含许多具有多种方法的类,可以用来执行大多数常用任务。大多数程序员认为是属于语言的一部份的功能都已转移到了框架类中。例如:假设要计算数字的平方根,在C#中不再提供用于计算平方根的函数,而是必须使用基类库中的System .Math .Sqrt方法。但是必须注意,基类库在所有.NET技术的语言中是共享的。Visual Basic、Ada甚至COBOL都将使用相同的基类函数来计算数字的平方根。这种方法使得基类功能在所有.NET支持的语言中高度一致,因为这些语言都以相似的方式调用平方根函数。
根据其功能,基类库中的类被归类到相应的命名空间中。这些命名空间被编译并被打包成程序集。就目前来说,可以将程序集看作一个.Dll文件,所有命名空间、类和方法都被打包到该文件中。
下面的表4.1中列出了最常用的命名空间和类
表4.1:常用命名空间和类
命名空间/类 |
内容 |
System |
BCL中的许多功能都包含在此命名空间中,它由其它命名空间组成。 |
System.Array类 |
包含用于操纵数组的方法。 |
System.Threading |
包含用于多线程处理的类。 |
System.Math |
包含用于执行数学函数的方法。 |
System.IO |
包含用于读取和写入文件的流的类。 |
System.Reflection |
包含用于从程序集中读取元数据的类。 |
System.Net |
包含用于Internet访问和套接字编程的类。 |
.NET框架SDK提供了一个名为WinCV的工具。该工具用于查看程序集以及游览程序集中的类。调用该工具的步骤将在附录B中讨论。
下面将就一些重要的基类进行讨论。首先从System.Array开始。
1.5.1 System.Array类
System.Array类提供了用于操纵数组的方法。在前面的章节中,我们已经学习了创建数组和为数组分配元素的方法。现在来回忆一下如何在C#中创建数组。
声明数组的语法如下所示。
DataType[] identifier;
因此,如果想创建一个可以包含7个元素且类型为int的数组,可以编写如下代码:
int[] myArray={1,2,3,4,5,6,7};
考虑下面的示例。
示例2:
using System;
class Test
{
static void Main()
{
int[] arrayToReverse={1,2,3,4,5,6,7};
Console.WriteLine(“反转前数组的内容:\n”);
//显示数组的内容
displayArray(arrayToReverse);
//反转数组内容
Array.Reverse(arrayToReverse);
Console.WriteLine(“\n\n反向后数组中的内容:\n”);
//显示反转后的内容
displayArray(arrayToReverse);
}
public static void displayArray(Array myArray)
{
foreach(int arrValue in myArray)
{
Console.WriteLine(arrValue);
}
}
}
使用Array类的Reverse方法反转数组。此类在命名空间的System中提从。
Reverse()方法的语法为:
Array.Revers(ArrayName);
示例3:
using System;
class Test
{
static void Main()
{
int[] arrayToUser={6,4,7,3,5,1,2};
Console.WriteLine(“输入一个数字[1至7之间:”);
Int noToSearch =Convert.ToInt16(Console.ReadLine());
Int indexNo=Array.IndexOf(arrayToUse,noToSearch);
If(indexNo= =6)
{
Console.WriteLine(“{0}是找到的最后一个元素,其位置是{1}”,noToSearch , indexNo);
}
else
{
Console.WirteLine(“不是最后一个元素,其位置是{0}”, indexNo);
}
}
}
Array.IndexOf()方法返回数组中匹配项的位置。方法如下:
Int Array.IndexOf(ArrayToSearch,WhatToSearch)
ArrayToSearch表示需要搜索的数组。
WhatToSearch表示需要在该数组中搜索的元素。
System.Array 类的其他重要方法及其功能如表6.2所示
表4.2 System.Array类的方法
方法 |
语法 |
功能 |
Clear |
void Array.Clear(ArrayName,int index,int length) |
将数组中一定范围的元素设置为零或空 |
Copy |
void |
此方法复制数组中从指定源索引开始的一定范围的元素,并将这些元素粘贴到另一个开始于指定目标索引处的数组中。 |
LastIndexOf |
Int Array.LastIndexOf(array.searchElement) |
返回数组中该值最后一个匹配项的索引 |
Sort |
Void Array.Sort(arrayName) |
对数组中的元素进行排序 |
1.5.2 命名空间 System.Threading
C#中我们使用空间System.Threading在程序中实现多线程。多线程是指同时运行同一程序或程序不同部份的一个或多个实例
我们来看看示例。
示例4:
using System;
using System.Threading;
class Test
{
static void Main()
{
Thread NewThread = new Thread(new ThreadStart(ThreadToRun));
newThread.Start();
ThreadToRun();
}
static void ThreadToRun()
{
for(int count=1;count<10;count++)
{
Console.WriteLine(“线程数为{0}”,count);
}
}
}
上面例子两个线程同时运行,因为都在向控制台输出语句,所以看起来有点乱。而且没有顺序的输出。为了解决这个问题,C#提供了锁机制。这也称为“线程同步”。
1.5.3 线程同步
线程同步是一种确保不同线程协调其对共享资源(例如:控制台)的访问方式。那么同步在C#中由关键字lock提供。锁机制确保同一时间只有一个线程可以访问某个方法或代码。我们再来看看示例:
示例5:
using System;
using System.Threading;
class Test
{
static void Main()
{
Test objTest=new Test();
Thread newThread = new Thread(new ThreadStart(objTest.threadToRun));
newThread.Start();
objTest.threadToRun();
}
void threadToRun()
{
lock(this)
for(int count=1;count<10;count++)
{
Console.WriteLine(“线程数为{0}”,count);
}
}
}
请注意,threadToRun()方法不再是静态的,因为需要从Test类的实例子访问该方法。
1.5.4 命名空间System.IO
命名空间System.IO提供了大量用于文件/流的输入/输出的类。此命名空间还提供了File类和Directory类,使用这些类可以对文件和目录进行操作。可以轻松地完成诸如复制、移动、重命名和删除之类的操作。
示例6:
using System;
using System.IO
class Test
{
static void Main(String[] args)
{
DirectoryInfo[] dirInfoArray;
FileInfo[] firleInfoArray;
Directory Info objDirInfo = new DirectoryInfo(“C:\\Program Files”);
dirInfoArray = objDirInfo.GetDirectories(“I*”);
fileInfoArray = objDirInfo.GetFiles(“*.*”);
foreach(DirectoryInfo dirInfo in dirInfoArray)
{
Console.WriteLine(dirInfo);
}
foreach(FileInfo fileInfo in fileInfoArray)
{
Console.WriteLine(fileInfo);
}
}
}
输出取决于运行该程序的系统中所安装的应用程序:
在示例6中,
创建了DirectoryInfo类(属于System.IO命名空间)的objDirInfo对象,该示例中将“c:\Program Files”(需要从该目录中检索目录名)作为目录名进行传递。
然后,使用DirectoryInfo类的GetDirectorise()方法来获取所有以I开头的目录,并将其填充到dirInfoArray中。
接着用Program Files目录中文件来填充数组fileInfoArray。必须使用objDirInfo对象的GetFiles()方法。
最后用循环取出两个数组的所有元素并打印在控制台上。
以上我们学习了如何列出目录中的文件。下面我们的讨论如何创建目录。
示例7:
using System;
using System.IO;
class Test
{
static void Main(String[] args)
{
Console.WriteLine(@”创建目录c:\Sample…”);
Directory.CreateDirectory(@”c:\Sample”);
DateTime creationDate = DireCtory.GetCreationTime(@”c:\Sample”);
Console.WriteLine(“目录创建于:”+ creationDate.ToString());
}
}
以上示例使用System.IO 命名空间中的Directory.CreateDirectory()方法来创建目录。如果仔细你可以注意到在被传递的字符串前有一个@符号作为前缀。这是因为在被传递的字符串中用到了转义符号“\”,转义序列有可能被编译器弄混淆,所以@符号用于重写所有转义序列。
Directory类的其它方法如下表。
表4.3:Directory类的方法
方法 |
语法 |
功能 |
Delete |
void Delete(stringPath) |
删除目录及其内容 |
Exists |
bool Exists(stringPath) |
检查给定的路径是否指向现有目录。 |
GetCurrentDirectory |
string GetCurrentDirectory(); |
获取应用程序的当前工作目录 |
Move |
void Move(stringSourceDirName, stringDestDirName); |
将目录及其内容移动到一个新的路径 |
1.5.5 System.String类
System.String类提供了很多用于操纵字符串的方法。通过这些方法,可以轻松的完成诸如复制、连接、替换、拆分、填充和大小写转换等操作。下面我们来看个例子。
示例8:
using System;
class Test
{
static void Main(String[] args)
{
String strOriginal, strToBeReplaced, strToReplace, strReplaced;
Console.WriteLine(“输入一个长字符串:”);
strOriginal = Console.ReadLine();
Console.WriteLine(“输入上一个字符串中将被替换的字符串:”);
strToBeReplaced = Console.ReadLine();
Console.WriteLine(“输入用于替换旧值的字符串:”);
strToReplace = Console.ReadLine();
strReplaced =strOriginal.Replace(strToBeReplaced,strToReplace);
Console.WriteLine(“新字符串:”+strReplaced);
}
}
以上示例中我们使用了System.String命名空间String类的Replace()方法替换了长字符串中的指定字符串。
我们来看看String类的其它一些重要的方法。
表4.4:String类的方法
方法 |
语法 |
功能 |
Copy |
String Copy(stringStr); |
此方法使用与指定字符串相同的值创建某个字符串的新实例。 |
EndsWith |
Bool EndsWith(string Value); |
此方法检查此实例的结尾是否与指定字符串相匹配。 |
PadLeft |
string PadLeft(int);string PadLeft(int,char); |
此方法使此实例中的字符右对齐,并用空格或指定的Unicode字符填充左端,以达到指定的总长度。 |
Split |
String Split(char[]);String Split(char[],int); |
此方法用于标识此实例中的子字符串(这些子字符串由数组中指定的一个或多个字符分隔),然后将子字符串放到一个类型为String的数组中。 |
此类有一个重要的特点,即这里的字符串实例表示一串不可变的字符。不可变意味着这些字符一旦创建就不能被修改。试图操纵字符串的方法实际会返回一个新的字符串。
1.5.6 System.Collections.ArrayList
ArrayList是专为数组而设计的版本。ArrayList就是一个集合。我们将在下一章中详细了解有关集合的信息。Array和ArrayList集合之间的一些主要区别如下:
在Array中一次只能获取或设置一个元素的值。而ArrayList提供了可以添加、插入或删除一定范围的元素的方法。
ArrayList具有 Capcity属性。此属性可以使ArrayList的容量自动扩展。如果属性ArrayList.Capacity的值被修改,则会自动进行内存的重新分配和元素的复制。这样,当有元素被添加到ArrayList中时,其容量在上限会动态增加。而Array的容量是固定的,因而不能被修改。
可以设置Array的下限,而ArrayList的下限始终为零。
Array可以有多维,而且ArrayList只有一维。
使用Synchronized方法可以很容易地创建ArrayList的同步版本。而Array 中,同步的实现是留给用户完成的。
ArrayList提供了可以向集合返回只读且大小固定的包装的方法。而Array没有提供这些方法。
看下这个示例,向ArrayList中添加元素。
示例9:
using System;
using System.Collections;
public class SimpleArrayList
{
public static void Main()
{
ArrayList myList = new ArrayList();
myList.Add(“欢迎”);
myList.Add(“培训”);
myList.Add(“C#”);
myList.Add(“!”);
Console.WriteLine(“\n\t 我的表单”);
Console.WriteLine(“\t----------------”);
Console.WriteLine(“\t数量{0}”,myList.Count);
Console.WirteLine(“\t容量{0}”,myList.Capacity);
Console.WriteLine(“\t内容:”);
System.Collections.IEnumerator myEnumerator = myList.GetEnumerator();
While(myEnumerator.MoveNext())
{
Console.Write(“{0}”,myEnumerator.Current);
Console.WriteLine();
}
}
}
以上示例中“\t”表示tab制表符。System.Collections.Ienumerator是枚举迭代器。
1.6 小结
除了可以避免命名冲突外,命名空间也被设计成帮助组织代码的元素。
命名空间可以进行嵌套,这意味着可以在一个命名空间内声明另一个命名空间。
命名空间是隐式公共的。不能通过指定访问修饰符来重写这一属性。
当在某个类的命名空间内使用该类时,可以只使用它的类名。这称为“非限定名称”也称为“简称”。
完全限定名是在类的名称之前加上包含该类的命名空间以及一个点操作符。
使用using命名空间指令,可以在不使用限定名的情况下,在类的命名空间之外使用这些类。
using命名空间指令必须在所有成员声明之前被声明。
using别名指令可用于从命名空间中提取一个组件(类或结构),并将其放入作用域中。
基类库是预先编写的代码的集合,这些代码可以很容易地合并到应用程序中,并在应用程序中使用。
System.Array类提供了用于操纵数组的方法。
可以使用命名空间System.Threading在程序中实现多线程。
命名空间System.IO提供了大量用于文件/流的输入/输出的类。
System.String类提供了用于操纵字符串的方法。
System.Collections.ArrayList类提供了用于操纵与普通数组略有不同的一维数组的方法。
1.7 练习
1. 限定名又可以称为“简称”。
a.对 b.错
2. 命名空间System 可用于_________________。
a. 与系统交互,包括控制台(屏幕)和键盘
b. 解决具有二义性的类名。
3. __________________指令可用于从命名空间中提取一个类。
a.using命名空间 b.using 别名
4. System.IO包含了用于从程序集中读取元数据的类。
a.对 b.错
5. Directory类的GetCreationTime方法返回的值的类型为_____________。
a.String b.DateTime
c.Date d.Time
4.8 作业
1. 创建一个命名空间,将下列类组织在命名空间中,这些类表示不同的方向。
North
South
East
West
2. 在上面的练习中,在East类中创建North类的对象。
3. 创建一个名为DirArray的字符串数组(有6个元素)。在初始化期间用一些名称(字符串)填充该数组。创建另一个名为Array2的数组(也有6个元素)。将DirArray中的元素进行排序并复制到Array2中。
(提示:使用Array.Sort方法)
4. 创建名为DirectManip的类。在该类中,编写出DirName方法,用于显示用户控制台上的当前目录的名称。
(提示:使用GetCurrentDirectory方法)
5. 在作业4创建的类中,编写另一个方法CreateDirect,该方法可以用作业3中DirArray数组中的元素值在c:\中创建目录。