[自序]
尽管Microsoft Visual Studio .NET 2005(过去好像叫Visual Studio .NET 2004)一再推迟其发布日期,但广大开发者对其的猜测以及各种媒体对其各方面的“曝光”也似乎已经充斥了网络。但与C#有关的文章似乎无外乎两个方面:VS.NET 2005 IDE特性、介绍C# 2.0中引入的“四大特性(泛型、匿名方法、迭代器和不完整类型)”。对IDE的研究我不想谈了,微软就是微软,他的窗口应用程序总是没得说的。而就语言本身的改进来说,在我听完了Anders Hejlsberg在Microsoft Professional Developers Conference 2003(2003.10, Los Angeles, CA)上的演讲后,发现除了这四大特性之外,还有一些鲜为人知的特性,甚至在微软官方的文档《C# Language Specification Version 2.0》中都没有提到。而这些特性却更加提高了语言的逻辑性。于是我编写了大量实际程序研究了这些特性,终于著成本文。本打算发表在《CSDN开发高手》杂志上的,可无奈水平有限,只能留在这个角落里贻笑大方了。希望能够对那些对C#语言有着浓厚兴趣的朋友有些帮助。
——lover_P 于北京工业大学1号楼221寝室
[修订说明]
2004-08-24
第一次修订。修改了大量的错别字和文法错误。添加了对global限定符的介绍。
[正文]
微软在其即将推出的C#2.0(Visual C# Whidbey)中,添加了许多令程序员感到振奋的新特性。除了泛型(Generic)、迭代器(Iterator)、匿名方法(Anonmynous)和不完整类型(Partial Type)等重大的改进,还对现有的语法细节进行了很大的改进,极大地方便了.NET框架程序设计的工作,并且进一步加强了C#语言独有的高逻辑性。在本文中,我将向大家介绍一下这些改进。(文中C#指代的是C#1.2及以前的版本,而C#2.0指代的是微软尚未正式推出的C# Whidbey;文章中的所有代码均在版本号为8.00.30703.4的C#编译器下进行了测试,标有*的错误消息得自版本号为7.10.3052.4的C#编译器。)
[内容]
静态类
属性的可访问性限定
命名空间别名
global限定符
编译器指令
固定大小缓冲区
参考文献
静态类
使用C#进行.NET框架程序设计的人应该都知道,无法将一个类声明为静态的。例如,下面的类声明:
public static class A {
static int i;
}
在C#中是无效的,当我们尝试编译这段代码时会得到下面的编译错误*:
error CS0106: 修饰符“static”对该项无效
由于无法用static修饰符修饰一个类,我们在类中总是能够既声明静态成员又声明实例成员。这无疑会带来很大的灵活性。但是,如果我们希望一个类是静态的,也就是希望强制要求这个类中的所有成员都应该为静态的,就无能为力了,唯一能做的就是自己注意将所有的成员声明为static。当我们忘记对一个本应是静态的成员使用static修饰符(尽管这是一个“低级错误”,但仍有可能发生)时,将会产生难以预料的错误。最重要的是,对于一个逻辑上的静态类(所有成员均使用static修饰符进行声明的类),我们甚至可以声明该类的一个变量并使用new操作符产生该类的实例!这显然不是我们所期望的。
而在C#2.0中,则提供了静态类这一概念,允许static修饰符对类进行修饰,上面的代码得以通过编译。如果一个类声明中包含了static修饰符,那么这个类中的所有成员将被强制要求声明为静态的。这时,如果我们故意在类中声明实例成员或是不小心忘记了成员声明中的static修饰符,如下面代码所示:
public static class A {
int i;
}
则编译器会报告错误:
error CS0708: 'A.i': cannot declare instance members in a static class
同时,如果我们声明该类的变量或是试图建立该类的一个实例时,如下面的代码:
public class Test {
A a; // error CS0723
void Foo() {
a = new A(); // error CS0712
}
}
则会得到下面的两个编译错误:
error CS0723: Cannot declare variable of static type 'A'
error CS0712: Cannot create an instance of the static class 'A'
很显然,C#2.0中对静态类的支持极大程度地避免了我们在书写程序中的意外失误,尤其是加强了静态类的逻辑性,提高了代码的可读性。
属性的可访问性限定
C#为我们提供了相当方便的属性定义,使得我们可以像访问类的公有变量成员那样访问类的属性,但还可以同时得到像访问函数那样的安全性。然而,C#只允许属性的设置动作(set{...})和获取动作(get{...})具有相同的可访问性(由属性声明中的public、internal和private等修饰符指定)。那么,当我们希望允许从任何程序集中的类获取一个特定类的属性,但只允许该类所在的程序集或该类的私有成员才能设置该属性时,我们只能将这个属性声明为公有且只读(即使用public修饰符声明但只有get{}域),而内部的或私有的成员只能通过设置与该属性相关的内部或私有的变量成员的值来完成属性的设置工作:
public class A {
int _intValue; // 与属性相关的一个int类型的成员变量
// 公有且只读的属性,允许任何类获取该属性的值:
public int Value {
get { return _intValue; }
}
// 下面的方法需要设置上面的属性,
// 但只能通过访问私有成员变量来完成,
// 并且要另外进行错误处理
private void SomeMethod() {
int i;
// ......
// 下面的if-else语句仅用来设置属性值:
if(0 < i && i < 10) {
_intValue = i;
}
else {
// 错误处理
}
}
}
很明显,这种做法是非常麻烦的。如果在多个地方改变了成员变量的值会使代码变得冗长不可读,还很有可能会产生错误,譬如该类有另外一个方法:
private void AnotherMethod() {
int i;
// ......
// 下面的if-else语句仅用于设置属性值,
// 但其对i的区间检测发生了错误
if(0 < i && i <= 10) { // 注意这里的 <= 运算符
_intValue = i;
}
// 并且没有进行错误处理
// ......
}
上面的方法对将要赋给私有变量成员的值的检查区间是错误的,这种错误是很有可能发生的。一旦调用了这个方法,_intValue很有可能具有错误的值,而访问了Value属性的外部程序集将会出现逻辑错误。这种错误的解决是相当困难的。并且,如果一个小组中的其他成员负责设计同一程序集中其他的类,要求他们在方法中书写如此大量的代码并要进行错误检查是不人道的。
当然,我们可能会想到将这种设置属性值的工作放到一个内部方法中集中进行:
// 程序集内部的类或该类的私有成员通过
// 下面的内部方法对上面的属性进行设置工作
internal void _setIntValue(int newValue) {
if(0 < newValue && newValue < 10) {