C#语言参考

C#语言参考

Copyright  Microsoft Corporation 1999-2002. All Rights Reserved.

注意

本文并非最终文档,在最终的商业版本发布前,可能会有重大修改。而且,它属微软公司所有。它是根据接受者和微软公司间的非公开协议公布的。

本文档只是为了报告的目的提供的,并且,在本文档中,微软公司并没有作任何明示或暗示的担保。对本文档中的内容进行更改不会给出提示。

用户要对由于使用本文档所造成的全部危险和后果负责。用户有责任遵守所有有效的版权法律。

虽然没有用版权来限制,但是如果没有Microsoft公司明确的书面许可,本文档的任何部分都不可以被复制、存储或引入检索系统,或用任何手段进行传播(电子的、机械的,影印,记录或其他方式)或其它任何用途。

微软拥有涉及本文档主要内容的专利、专有应用程序、商标、版权和其它知识产权。除非有微软公司提供的任何书面的许可,提供本文档并没有给你任何关于这些专利、商标、版权和其它知识产权的许可。

未出版作品。 © 1999-2002 Microsoft 公司。版权所有。

Microsoft, Windows, Visual Basic, and Visual C++是微软公司在美国和/或其他国家或地区的注册商标或商标。

这里所提及的其它产品和公司的名称可能是他们各自所有者的商标。

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Table of Contents

目录

1. 介绍....................................................................................................................................................................1

1.1 Hello, world..................................................................................................................................................1

1.2 类型..............................................................................................................................................................2

1.2.1 预定义类型...........................................................................................................................................3

1.2.2 转换.......................................................................................................................................................5

1.2.3 数组类型...............................................................................................................................................6

1.2.4 类型系统一致.......................................................................................................................................7

1.3 变量和参数..................................................................................................................................................8

1.4 自动内存管理............................................................................................................................................11

1.5 表达式........................................................................................................................................................13

1.6 声明............................................................................................................................................................15

1.7 类................................................................................................................................................................18

1.7.1 常数.....................................................................................................................................................20

1.7.2 域.........................................................................................................................................................20

1.7.3 方法.....................................................................................................................................................21

1.7.4 属性.....................................................................................................................................................22

1.7.5 事件.....................................................................................................................................................23

1.7.6 操作符.................................................................................................................................................24

1.7.7 索引.....................................................................................................................................................25

1.7.8 实例构造函数.....................................................................................................................................26

1.7.9 析构函数.............................................................................................................................................27

1.7.10 静态构造函数...................................................................................................................................27

1.7.11 继承...................................................................................................................................................28

1.8 结构............................................................................................................................................................29

1.9 接口............................................................................................................................................................30

1.10 代表..........................................................................................................................................................31

1.11 联合..........................................................................................................................................................32

1.12 名称空间和集合......................................................................................................................................32

1.13 版本..........................................................................................................................................................34

1.14 特征..........................................................................................................................................................36

2. 语法结构..........................................................................................................................................................39

2.1 翻译的阶段................................................................................................................................................39

2.2 文法符号....................................................................................................................................................39

2.3 预处理........................................................................................................................................................40

2.3.1 预处理声明.........................................................................................................................................40

2.3.2 #if, #elif, #else, #endif..........................................................................................................................41

2.3.3 预处理控制行.....................................................................................................................................42

2.3.4 #line.....................................................................................................................................................43

2.3.5 预处理标识符.....................................................................................................................................43

2.3.6 预处理表达式.....................................................................................................................................43

2.3.7 与空白交互作用.................................................................................................................................44

2.4 语法分析....................................................................................................................................................45 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. iii

C# LANGUAGE REFERENCE

2.4.1 输入.....................................................................................................................................................45

2.4.2 输入字符.............................................................................................................................................45

2.4.3 行结束符.............................................................................................................................................45

2.4.4 注释.....................................................................................................................................................45

2.4.5 空白.....................................................................................................................................................46

2.4.6 标记.....................................................................................................................................................47

2.5 句法分析....................................................................................................................................................47

2.5.1 标识符.................................................................................................................................................47

2.5.2 关键字.................................................................................................................................................48

2.5.3 数据符号.............................................................................................................................................48

2.5.4 操作符和标点.....................................................................................................................................53

2.5.5 Unicode 字符转意字符序列................................................................................................................53

3. 基本概念..........................................................................................................................................................55

3.1 声明............................................................................................................................................................55

3.2 成员............................................................................................................................................................57

3.2.1 名称空间成员.....................................................................................................................................57

3.2.2 结构成员.............................................................................................................................................57

3.2.3 枚举成员.............................................................................................................................................57

3.2.4 类成员.................................................................................................................................................58

3.2.5 接口成员.............................................................................................................................................58

3.2.6 数组成员.............................................................................................................................................58

3.2.7 代表成员.............................................................................................................................................58

3.3 成员访问....................................................................................................................................................58

3.3.1 声明可访问性.....................................................................................................................................58

3.3.2 可访问性域.........................................................................................................................................59

3.3.3 保护的访问.........................................................................................................................................61

3.3.4 可访问性约束.....................................................................................................................................62

3.4 签名和重载................................................................................................................................................62

3.5 范围............................................................................................................................................................63

3.5.1 名称隐藏.............................................................................................................................................65

3.6 名称空间或类型名称................................................................................................................................67

3.6.1 合格的名称.........................................................................................................................................68

4. 类型..................................................................................................................................................................69

4.1 数值类型....................................................................................................................................................69

4.1.1 默认构造函数.....................................................................................................................................70

4.1.2 结构类型.............................................................................................................................................71

4.1.3 简单类型.............................................................................................................................................71

4.1.4 整数类型.............................................................................................................................................72

4.1.5 浮点类型.............................................................................................................................................73

4.1.6 十进制类型.........................................................................................................................................74

4.1.7 布尔类型...........................................................................................................................................74

4.1.8 枚举类型.............................................................................................................................................74

4.2 引用类型....................................................................................................................................................74

4.2.1 类类型.................................................................................................................................................75 iv Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Table of Contents

4.2.2 对象类型.............................................................................................................................................75

4.2.3 字符串类型.........................................................................................................................................75

4.2.4 接口类型.............................................................................................................................................76

4.2.5 数组类型.............................................................................................................................................76

4.2.6 代表类型.............................................................................................................................................76

4.3 包装和解包................................................................................................................................................76

4.3.1 包装转换.............................................................................................................................................76

4.3.2 解包转换.............................................................................................................................................77

5. 变量..................................................................................................................................................................79

5.1 变量类型....................................................................................................................................................79

5.1.1 静态变量.............................................................................................................................................79

5.1.2 实例变量.............................................................................................................................................79

5.1.3 数组元素.............................................................................................................................................80

5.1.4 数值参数.............................................................................................................................................80

5.1.5 引用参数.............................................................................................................................................80

5.1.6 输出参数.............................................................................................................................................80

5.1.7 局部变量.............................................................................................................................................81

5.2 默认值........................................................................................................................................................81

5.3 明确赋值....................................................................................................................................................81

5.3.1 初始赋值变量.....................................................................................................................................83

5.3.2 非初始赋值变量.................................................................................................................................84

5.4 变量引用....................................................................................................................................................84

6. 转换..................................................................................................................................................................85

6.1 隐式转换....................................................................................................................................................85

6.1.1 同一性转换.........................................................................................................................................85

6.1.2 隐式数值转换.....................................................................................................................................85

6.1.3 隐式枚举转换.....................................................................................................................................86

6.1.4 隐式引用转换.....................................................................................................................................86

6.1.5 转换.....................................................................................................................................................86

6.1.6 隐式常数表达式转换..........................................................................................................................86

6.1.7 用户自定义隐式转换..........................................................................................................................87

6.2 显式转换....................................................................................................................................................87

6.2.1 显式数值转换.....................................................................................................................................87

6.2.2 显式枚举类型转换..............................................................................................................................88

6.2.3 显式引用类型转换..............................................................................................................................88

6.2.4 解包转换.............................................................................................................................................89

6.2.5 用户自定义显式转换..........................................................................................................................89

6.3 标准转换....................................................................................................................................................89

6.3.1 标准隐式转换.....................................................................................................................................89

6.3.2 标准显式转换.....................................................................................................................................90

6.4 用户定义转换............................................................................................................................................90

6.4.1 允许的用户自定义转换.......................................................................................................................90

6.4.2 用户自定义转换的取值.......................................................................................................................90

6.4.3 用户自定义隐式转换..........................................................................................................................91 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. v

C# LANGUAGE REFERENCE

6.4.4 用户自定义显式类型转换...................................................................................................................91

7. 表达式..............................................................................................................................................................93

7.1 表达式分类................................................................................................................................................93

7.1.1 表达式的数值.....................................................................................................................................94

7.2 操作符........................................................................................................................................................94

7.2.1 操作符优先级和结合顺序...................................................................................................................94

7.2.2 操作符重载.........................................................................................................................................96

7.2.3 一元操作符重载分析..........................................................................................................................97

7.2.4 二元操作符重载分析..........................................................................................................................97

7.2.5 候选用户定义操作符..........................................................................................................................98

7.2.6 数字升级.............................................................................................................................................98

7.3 成员查询....................................................................................................................................................99

7.3.1 基类型...............................................................................................................................................100

7.4 函数成员..................................................................................................................................................100

7.4.1 参数列表...........................................................................................................................................102

7.4.2 重载分析...........................................................................................................................................103

7.4.3 功能成员调用...................................................................................................................................105

7.4.4 虚拟函数成员查找............................................................................................................................106

7.4.5 接口函数成员查找............................................................................................................................106

7.5 主要的表达式..........................................................................................................................................106

7.5.1 文字...................................................................................................................................................106

7.5.2 简单名称...........................................................................................................................................107

7.5.3 加括号的表达式...............................................................................................................................108

7.5.4 成员访问...........................................................................................................................................108

7.5.5 调用表达式.......................................................................................................................................110

7.5.6 元素访问...........................................................................................................................................112

7.5.7 This访问............................................................................................................................................113

7.5.8 基本访问...........................................................................................................................................114

7.5.9 递增和递减后缀操作符.....................................................................................................................114

7.5.10 new操作符......................................................................................................................................115

7.5.11 typeof操作符.............................................................................................................................119

7.5.12 sizeof操作符............................................................................................................................119

7.5.13 checked和unchecked操作符.................................................................................................120

7.6 一元表达式..............................................................................................................................................122

7.6.1 一元正值运算符...............................................................................................................................122

7.6.2 一元负值运算符...............................................................................................................................122

7.6.3 逻辑非运算符...................................................................................................................................123

7.6.4 按位求补运算符...............................................................................................................................123

7.6.5 间接运算符.......................................................................................................................................123

7.6.6 地址运算符.......................................................................................................................................123

7.6.7 前缀增量和减量运算符.....................................................................................................................123

7.6.8 强制类型转换表达式........................................................................................................................124

7.7 算术运算符..............................................................................................................................................125

7.7.1 乘法运算符.......................................................................................................................................125 vi Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Table of Contents

7.7.2 除法运算符.......................................................................................................................................126

7.7.3 余数运算符.......................................................................................................................................127

7.7.4 加法运算符.......................................................................................................................................127

7.7.5 减法运算符.......................................................................................................................................129

7.8 移位运算符..............................................................................................................................................130

7.9 关系运算符..............................................................................................................................................131

7.9.1 整数比较运算符...............................................................................................................................132

7.9.2 浮点比较运算符...............................................................................................................................132

7.9.3 小数比较运算符...............................................................................................................................133

7.9.4 布尔相等运算符...............................................................................................................................133

7.9.5 枚举比较运算符...............................................................................................................................133

7.9.6 引用类型相等运算符........................................................................................................................133

7.9.7 字符串相等运算符............................................................................................................................135

7.9.8 代表相等运算符...............................................................................................................................135

7.9.9 is 运算符............................................................................................................................................135

7.10 逻辑运算符............................................................................................................................................136

7.10.1 整数逻辑运算符..............................................................................................................................136

7.10.2 枚举逻辑运算符..............................................................................................................................136

7.10.3 布尔逻辑运算符..............................................................................................................................137

7.11 条件逻辑运算符....................................................................................................................................137

7.11.1 布尔条件逻辑运算符.......................................................................................................................137

7.11.2 用户自定义的条件逻辑运算符.......................................................................................................138

7.12 条件运算符............................................................................................................................................138

7.13 赋值运算符............................................................................................................................................139

7.13.1 简单赋值.........................................................................................................................................139

7.13.2 组合赋值.........................................................................................................................................141

7.13.3 事件赋值.........................................................................................................................................142

7.14 表达式....................................................................................................................................................142

7.15 常量表达式............................................................................................................................................142

7.16 布尔表达式............................................................................................................................................143

8. 语句................................................................................................................................................................145

8.1 终点与可达性..........................................................................................................................................145

8.2 块 146

8.2.1 语句列表...........................................................................................................................................147

8.3 空语句......................................................................................................................................................147

8.4 标号语句..................................................................................................................................................148

8.5 声明语句..................................................................................................................................................148

8.5.1 局部变量声明...................................................................................................................................148

8.5.2 局部常量声明...................................................................................................................................149

8.6 表达式语句..............................................................................................................................................150

8.7 选择语句..................................................................................................................................................150

8.7.1 if语句................................................................................................................................................150

8.7.2 Switch 语句.......................................................................................................................................151

8.8 重复语句..................................................................................................................................................154 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. vii

C# LANGUAGE REFERENCE

8.8.1 while语句..........................................................................................................................................155

8.8.2 do语句...............................................................................................................................................155

8.8.3 for语句..............................................................................................................................................155

8.8.4 foreach语句.......................................................................................................................................157

8.9 跳转语句..................................................................................................................................................158

8.9.1 break语句..........................................................................................................................................158

8.9.2 continue语句.....................................................................................................................................159

8.9.3 goto语句............................................................................................................................................159

8.9.4 return语句.........................................................................................................................................160

8.9.5 throw语句.........................................................................................................................................160

8.10 try语句...................................................................................................................................................161

8.11 checked和unchecked语句....................................................................................................................163

8.12 lock语句................................................................................................................................................163

9. 名称空间........................................................................................................................................................165

9.1 编译单元..................................................................................................................................................165

9.2 名称空间声明..........................................................................................................................................165

9.3 使用指示..................................................................................................................................................166

9.3.1 使用别名指示...................................................................................................................................167

9.3.2 使用名称空间指示............................................................................................................................169

9.4 名称空间成员..........................................................................................................................................170

9.5 类型声明..................................................................................................................................................171

10. 类..................................................................................................................................................................173

10.1 类声明....................................................................................................................................................173

10.1.1 类修饰符.........................................................................................................................................173

10.1.2 类基础规范.....................................................................................................................................174

10.1.3 类主体.............................................................................................................................................176

10.2 类成员....................................................................................................................................................176

10.2.1 继承.................................................................................................................................................177

10.2.2 new修饰符......................................................................................................................................177

10.2.3 访问修饰符.....................................................................................................................................178

10.2.4 要素类型.........................................................................................................................................178

10.2.5 静态和实例成员..............................................................................................................................178

10.2.6 嵌套类型.........................................................................................................................................179

10.3 常数........................................................................................................................................................179

10.4 域............................................................................................................................................................180

10.4.1 静态和实例域.................................................................................................................................181

10.4.2 只读域.............................................................................................................................................182

10.4.3 域的初始化.....................................................................................................................................183

10.4.4 变量初始化函数..............................................................................................................................183

10.5 方法........................................................................................................................................................185

10.5.1 方法参数.........................................................................................................................................186

10.5.2 静态和实例方法..............................................................................................................................189

10.5.3 虚拟方法.........................................................................................................................................190

10.5.4 覆盖方法.........................................................................................................................................191 viii Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Table of Contents

10.5.5 抽象方法.........................................................................................................................................193

10.5.6 外部方法.........................................................................................................................................194

10.5.7 方法主体.........................................................................................................................................195

10.5.8 方法重载.........................................................................................................................................195

10.6 属性........................................................................................................................................................195

10.6.1 静态属性.........................................................................................................................................196

10.6.2 虚拟属性.........................................................................................................................................196

10.6.3 覆盖属性.........................................................................................................................................197

10.6.4 抽象属性.........................................................................................................................................198

10.6.5 访问符.............................................................................................................................................198

10.7 事件........................................................................................................................................................203

10.8 索引........................................................................................................................................................206

10.8.1 索引重载.........................................................................................................................................209

10.8.2 虚拟索引.........................................................................................................................................209

10.8.3 覆盖索引.........................................................................................................................................209

10.8.4 抽象索引.........................................................................................................................................210

10.9 操作符....................................................................................................................................................210

10.9.1 一元操作符.....................................................................................................................................211

10.9.2 二元操作符.....................................................................................................................................211

10.9.3 转换操作符.....................................................................................................................................212

10.10 实例构造函数......................................................................................................................................213

10.10.1 构造函数初始化函数.....................................................................................................................214

10.10.2 实例变量初始化函数.....................................................................................................................214

10.10.3 构造函数执行...............................................................................................................................214

10.10.4 默认构造函数...............................................................................................................................216

10.10.5 私有构造函数...............................................................................................................................217

10.10.6 可选的构造函数参数.....................................................................................................................217

10.11 析构函数..............................................................................................................................................217

10.12 静态构造函数......................................................................................................................................218

10.12.1 类加载和初始化............................................................................................................................219

11. 结构..............................................................................................................................................................221

11.1 结构声明................................................................................................................................................221

11.1.1 结构修饰符.....................................................................................................................................221

11.1.2 接口.................................................................................................................................................221

11.1.3 结构体.............................................................................................................................................221

11.2 结构成员................................................................................................................................................221

11.3 结构例子................................................................................................................................................221

11.3.1 数据库整数类型..............................................................................................................................221

11.3.2 数据库布尔类型..............................................................................................................................223

12. 数组..............................................................................................................................................................225

12.1 数组类型................................................................................................................................................225

12.1.1 System.Array 类型...................................................................................................................226

12.2 数组创建................................................................................................................................................226

12.3 数组元素访问........................................................................................................................................226 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. ix

C# LANGUAGE REFERENCE

12.4 数组成员................................................................................................................................................226

12.5 数组协方差............................................................................................................................................226

12.6 数组初始化函数....................................................................................................................................227

13. 接口..............................................................................................................................................................229

13.1 接口声明................................................................................................................................................229

13.1.1 接口修饰符.....................................................................................................................................229

13.1.2 基本接口.........................................................................................................................................229

13.1.3 接口主体.........................................................................................................................................230

13.2 接口成员................................................................................................................................................230

13.2.1 接口方法.........................................................................................................................................231

13.2.2 接口属性.........................................................................................................................................231

13.2.3 接口事件.........................................................................................................................................232

13.2.4 接口索引.........................................................................................................................................232

13.2.5 接口成员访问.................................................................................................................................232

13.3 完全有效的接口成员名称.....................................................................................................................234

13.4 接口实现................................................................................................................................................234

13.4.1 显式接口成员实现程序...................................................................................................................235

13.4.2 接口映射.........................................................................................................................................237

13.4.3 接口实现程序继承...........................................................................................................................239

13.4.4 接口重新实现程序...........................................................................................................................240

13.4.5 抽象类和接口.................................................................................................................................242

14. 枚举..............................................................................................................................................................243

14.1 枚举声明................................................................................................................................................243

14.2 枚举成员................................................................................................................................................244

14.3 枚举数值和操作....................................................................................................................................246

15. 代表..............................................................................................................................................................247

15.1 代表声明................................................................................................................................................247

15.1.1 可合并的代表类型...........................................................................................................................248

15.2 代表实例化............................................................................................................................................248

15.3 多点传送代表........................................................................................................................................248

15.4 代表调用................................................................................................................................................248

16. 异常..............................................................................................................................................................249

16.1 异常的产生............................................................................................................................................249

16.2 System.Exception 类......................................................................................................................249

16.3 异常怎样被处理....................................................................................................................................249

16.4 通用异常类............................................................................................................................................250

17. 属性..............................................................................................................................................................251

17.1 属性类....................................................................................................................................................251

17.1.1 AttributeUsage 属性..............................................................................................................251

17.1.2 位置的和名称的参数.......................................................................................................................252

17.1.3 属性参数类型.................................................................................................................................252

17.2 规范........................................................................................................................................................253 x Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Table of Contents

17.3 属性实例................................................................................................................................................255

17.3.1 一个属性的编译..............................................................................................................................255

17.3.2 一个属性实例的运行时检索...........................................................................................................256

17.4 保留的属性............................................................................................................................................256

17.4.1 AttributeUsage 属性..............................................................................................................256

17.4.2 条件属性.........................................................................................................................................257

17.4.3 废弃的属性.....................................................................................................................................259

18. 危险代码......................................................................................................................................................261

18.1 危险代码................................................................................................................................................261

18.2 指针类型................................................................................................................................................261

19. 互用性..........................................................................................................................................................263

19.1 COMImport 属性................................................................................................................................263

19.2 COMRegisterFunction 属性............................................................................................................263

19.3 COMSourceInterfaces 属性............................................................................................................264

19.4 COMVisible 属性...............................................................................................................................264

19.5 DispId 属性.......................................................................................................................................264

19.6 DllImport 属性................................................................................................................................264

19.7 FieldOffset 属性.............................................................................................................................265

19.8 GlobalObject 属性...........................................................................................................................265

19.9 Guid 属性...........................................................................................................................................266

19.10 HasDefaultInterface 属性.........................................................................................................266

19.11 ImportedFromTypeLib 属性.........................................................................................................266

19.12 In 和 Out 属性.................................................................................................................................266

19.13 InterfaceType 属性.......................................................................................................................267

19.14 MarshalAs 属性...............................................................................................................................267

19.15 NoIDispatch 属性...........................................................................................................................268

19.16 NonSerialized 属性.......................................................................................................................268

19.17 Predeclared 属性...........................................................................................................................268

19.18 PreserveSig 属性...........................................................................................................................268

19.19 Serializable 属性.........................................................................................................................269

19.20 StructLayout 属性.........................................................................................................................269

19.21 TypeLibFunc 属性...........................................................................................................................269

19.22 TypeLibType 属性...........................................................................................................................269

19.23 TypeLibVar 属性.............................................................................................................................270

19.24 支持的枚举..........................................................................................................................................270

20. 参考..............................................................................................................................................................273

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. xi

 

Chapter 错误!未定义样式。 错误!未定义样式。

1. 介绍

C#是一种简单、现代、面向对象和类型安全的编程语言,由C和C++发展而来。C#(发音为“C霎普”)牢固地植根于C和C++语言族谱中,并且会很快被C和C++程序员所熟悉。C#的目标在于把Visual Basic的高生产力和C++本身的能力结合起来。

C#作为Microsoft Visual Studio 7.0的一部分提供给用户。除了C#以外,Visual Studio还支持Visual Basic、Visual C++和描述语言VBScript和Jscript。所有这些语言都提供对Microsoft .NET平台的访问能力,它包括一个通用的执行引擎和一个丰富的类库。Microsoft .NET平台定义了一个“通用语言子集”(CLS),是一种混合语言,它可以增强CLS兼容语言和类库间的无缝协同工作能力。对于C#开发者,这意味着既是C#是一种新的语言,它已经可以对用老牌工具如Visual Basic和Visual C++使用的丰富类库进行完全访问。C#自己并没有包含一个类库。

本章的其余部分描述这种语言的基本特性。以后的章节将会详细描述规则和例外,并且有些时候以数学的方式描述,而这章会致力于对整体的简单而清楚地介绍。这样的目的是给读者一个关于语言的介绍,这样可以使读者可以更容易地开始编写程序和继续阅读后面的章节。

1.1 Hello, world

规范的“Hello,World”程序可以按照下面例子编写:

using System;

class Hello { static void Main() { Console.WriteLine("Hello, world"); } }

C#程序的源代通常存储在一个或多个扩展名为.cs的文件中,例如hello.cs。如果使用Visual Studio提供的命令行编译器,这样的程序可以用命令行命令来编译

csc hello.cs

这样就会生成一个名为hello.exe的可执行程序。程序的输出如下:

Hello, world

下面对这个程序进行详细的研究:

• 使用System;指令涉及到一个名称空间(namespace)叫作System,这是在Microsoft .NET类库中提供的。这个名称空间包括在Mian方法中使用的Console类。名称空间提供了一种用来组织一个类库的分层方法。使用“using”命令后,就可以无障碍地使用名称空间中的各种类型成员。“Hello,world”程序中使用的Console.WriteLine是System.Console.WriteLine的简写。

• Main方法是类Hello中的一个成员,它有static的说明符,所以它是类Hello中的一个方法而不是此类中的实例。

• 对于一个应用程序的主入口点-称开始执行的方法-通常是一个称为Main的静态方法。

• “Hello,world”的输出是通过使用类库产生的。语言本身并没有提供类库。作为替代,它使用一个通用类库,这个类库也可以被诸如Visual Basic和Visual C++的语言所使用。 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 1

C# LANGUAGE REFERENCE

对于C和C++开发者来说,会有兴趣知道对一些没有出现在“Hello,world”程序的东西。

• 程序没有把Main设为全局方法。在全局级别上不支持方法和变量;这些元素通常包含在类型声明当中(例如,类或结构的声明)。

• 程序中不使用“::”或“->”操作符。“::”不再是一个操作符,而“->”操作符也只是在程序的某个小片断中才会使用。操作符“.”用于符合名称,例如Console.WriteLine。

• 程序中不包括前向声明。因为声明的顺序不重要,所以不再需要前向声明。

• 程序中不使用#include关键字。程序中的从属关系是象征性的而不是字面上地。这个系统消除了在用不同语言编写的程序间的障碍。例如,Console类可以用另外一种语言编写。

1.2 类型

C#支持两种类型:数据类型和引用类型。数据类型包括一些简单类型(例如,char、int和float),枚举类型和结构类型。引用类型包括类类型、接口类型、代表(delegate)类型和数组类型。

数据类型和引用类型的区别在于,数据类型变量直接包含它们的数据,然而引用类型数据是存储对于对象的引用。对于引用类型,有可能两个变量引用相同的对象,因而可能出现对一个变量的操作影响到其它变量所引用对象的情况。对于数据类型,每个变量都有它们自己对数据的拷贝,所以不太可能因为对一个进行操作而影响到其它变量。

例子

using System;

class Class1 { public int Value = 0; }

class Test { static void Main() { int val1 = 0; int val2 = val1; val2 = 123;

Class1 ref1 = new Class1(); Class1 ref2 = ref1; ref2.Value = 123;

Console.WriteLine("Values: {0}, {1}", val1, val2); Console.WriteLine("Refs: {0}, {1}", ref1.Value, ref2.Value); } }

从此例可以说明两者间的不同。程序的输出如下

Values: 0, 123 Refs: 123, 123

对局部变量val1的赋值没有影响到局部变量val2,因为两个局部变量都是数据类型(int类型),并且每个数据类型的局部变量都有它们自己的存储。与此相对的是,对于ref.Value的赋值ref.Value=123对ref1和ref2都有影响。

这两行

Console.WriteLine("Values: {0}, {1}", val1, val2); Console.WriteLine("Refs: {0}, {1}", ref1.Value, ref2.Value); 2 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

值得更多的注释,因为它们可以让我们看到Console.WriteLine的一些字符串格式输出方式,这些实际上是使用了一些变量。第一个变量是一个字符串,它包含一些数字的占位符如{0}和{1}。每个占位符指向一个变量。占位符{0}指向第二个变量,占位符{1}指向第三个变量,等等。在输出被送到控制台前,这些占位符会被它们相对应的变量所替换。

开发者可以通过枚举和结构声明定义新数据类型,可以通过类、接口和代表声明来定义新引用类型。例子

using System;

public enum Color { Red, Blue, Green }

public struct Point { public int x, y; }

public interface IBase { void F(); }

public interface IDerived: IBase { void G(); }

public class A { protected void H() { Console.WriteLine("A.H"); } }

public class B: A, IDerived { public void F() { Console.WriteLine("B.F, implementation of IDerived.F"); }

public void G() { Console.WriteLine("B.G, implementation of IDerived.G"); }

override protected void H() { Console.WriteLine("B.H, override of A.H"); } }

public delegate void EmptyDelegate();

这里用一两个例子说明每种类型声明,以后的章节里会更详细地描述类型声明。

1.2.1 预定义类型

C#提供了一系列预定义类型,其中大多数对C和C++程序员来说都是比较熟悉的。

预定义引用类型是对象和字符串。类型对象是所有其它类型的最根本的基础类型,而类型字符串要用来说明Unicode字符串数据。 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 3

C# LANGUAGE REFERENCE

预定义数据类型包括有符号和无符号整数类型、浮点数类型、二进制、字符和十进制类型。有符号整数类型有sbyte、short、int和long;无符号整数类型有byte、ushort、uint和ulong;而浮点类型有float和double。

二进制类型用来表示二进制数据:值或者是真或者是假。包含二进制使得编写自说明代码变得容易,并且也帮助消除所有由于程序员在应当使用“==”时错误的使用了“=”造成的很普通的C++代码错误。在C#中,下面的例子

int i = ...; F(i); if (i = 0) // Bug: the test should be (i == 0) G();

是非法的,因为表达式i=0的类型是int,而if声明需要一个二进制类型的表达式。

Char类型用来说明Unicode字符。某个char类型变量说明一个单16位Unicode字符。

十进制类型适合应用在不能接受舍入误差计算中。通常的例子包括商业计算,例如税收计算和货币转换。十进制类型提供了28个有效位。

下面的表中列出了预定义类型,并且指出了如何为每一个类型赋值。

类型

描述

例子

object

所有其它类型的最根本的基础类型

object o = null;

string

字符串类型;一个字符传是一个Unicode字符序列

string s = "Hello";

sbyte

8-bit 有符号整数类型

sbyte val = 12;

short

16-bit有符号整数类型

short val = 12;

int

32-bit 有符号整数类型

int val = 12;

long

64-bit有符号整数类型

long val1 = 12; long val2 = 34L;

byte

8-bit 无符号整数类型

byte val1 = 12; byte val2 = 34U;

ushort

16-bit无符号整数类型

ushort val1 = 12; ushort val2 = 34U;

uint

32-bit无符号整数类型

uint val1 = 12; uint val2 = 34U;

ulong

64-bit无符号整数类型

ulong val1 = 12; ulong val2 = 34U; ulong val3 = 56L; ulong val4 = 78UL;

float

单精度浮点数类型

float val = 1.23F;

double

双精度浮点数类型

double val1 = 1.23; double val2 = 4.56D;

bool

二进制类型; 一个二进制数据不是真就是假

bool val1 = true; bool val2 = false;

char

字符类型; 一个字符数据是一个Unicode字符

char val = 'h';

decimal

精确十进制类型,有28个有效位

decimal val = 1.23M;

4 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

每一个预定义类型都是某个系统提供的类型的简写。例如,关键词int是一个名为System.Int32结构的简写。虽然更好的考虑是使用关键词而不是完全的系统类型名称,但是这两个名称可以交换使用。

例如int的预定义数据类型在某些地方被认为是特别的,但在大多数地方会像其它结构一样被正确对待。操作符重载使得编程人员可以定义同预定义数据类型行为相同的类型。例如,一个Digit结构可以支持与整数类型相同的数学操作,并且可以定义Digit和预定义类型间的转换。

预定义类型可以允许操作符重载它们自己。例如,比较符==和!=对应不同的预定义类型有不同的语意:

• 如果两个int类型的表达式代表了相同的整数据,它们被认为是相等的。

• 如果两个object类型的表达式都指向相同的对象或者都是空的,它们被认为是相等的。

• 如果字符串实例有相同的长度并且在每个字符的位置都相同,或者都为空,这两个字符串类型的表达式就被认为是相等的。

例子

class Test { static void Main() { string s = "Test"; string t = string.Copy(s); Console.WriteLine(s == t); Console.WriteLine((object)s == (object)t); } }

产生下面的输出

True False

因为第一个比较符比较两个string类型的表达式,而第二个比较符比较两个object类型的表达式。

1.2.2 转换

有两种类型的转换隐式转换和显式转换。隐式转换应用于需要小心地仔细检查就可以安全地实现的转换。例如,从int到long就是一个隐式转换。隐式转换通常都能成功,并且不会带来失去信息的后果。就像例子中所示,隐式转换可以隐式地实现。

using System;

class Test { static void Main() { int intValue = 123; long longValue = intValue; Console.WriteLine("(long) {0} = {1}", intValue, longValue); } }

这里隐式地把int转换为long。

相反,显式转换必须要个安排好的表达式才能实现。例子

using System;

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 5

C# LANGUAGE REFERENCE

class Test { static void Main() { long longValue = Int64.MaxValue; int intValue = (int) longValue; Console.WriteLine("(int) {0} = {1}", longValue, intValue); } }

用显式转换把long转换到int。输出为:

(int) 9223372036854775807 = -1

因为发生溢出。

1.2.3 数组类型

数组可以是一维或多维。支持“矩形”数组也支持“不规则”数组。

一维数组是最普通的类型,所以,从这里开始讲是个不错的开始。例子

using System;

class Test { static void Main() { int[] arr = new int[5];

for (int i = 0; i < arr.Length; i++) arr[i] = i * i;

for (int i = 0; i < arr.Length; i++) Console.WriteLine("arr[{0}] = {1}", i, arr[i]); } }

创建一个一维int数据数组,初始化数组元素并且把每个元素打印出来。程序的输出为:

arr[0] = 0 arr[1] = 1 arr[2] = 4 arr[3] = 9 arr[4] = 16

前面例子中使用的int[]类型是一个数组类型。数组类型的写法是前面一个非数组类型,其后跟一个或多对中括号。例子

class Test { static void Main() { int[] a1; // single-dimensional array of int int[,] a2; // 2-dimensional array of int int[,,] a3; // 3-dimensional array of int

int[][] j2; // "jagged" array: array of (array of int) int[][][] j3; // array of (array of (array of int)) } }

介绍了各种使用int类型元素的数组类型定义局部变量的方法。

数组是引用类型,所以声明一个数组变量只是为对此数组的引用设置了空间。数组实例的实际创建是通过数组初始化程序和数组创建表达式。例子 6 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

class Test { static void Main() { int[] a1 = new int[] {1, 2, 3}; int[,] a2 = new int[,] {{1, 2, 3}, {4, 5, 6}}; int[,,] a3 = new int[10, 20, 30];

int[][] j2 = new int[3][]; j2[0] = new int[] {1, 2, 3}; j2[1] = new int[] {1, 2, 3, 4, 5, 6}; j2[2] = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9}; } }

给出了各种数组创建表达式.变量a1、a2和a3表示矩形数组,而 变量j2表示了一个不规则数组。这里没必要对在这里按照数组的形状分类感到惊讶。矩形数组通常有矩形的形状,给出数组每一维的长度,它的矩形形状就清楚了。例如,a3的三维长度分别为10、20和30,并且不难看出这个数组包括10*20*30个元素。

相反,变量j2表示一个“不规则”数组,或者称为“数组的数组”。特别是,j2表示了一个int数组组成的数组,或者说是一个一维int[]类型的数组。这些int[]变量中的每一个都可以独自被初始化,同时允许数组有一个不规则形状。例子中为每个int[]数组定义了不同的长度。很清楚地可以看到,j2[0]的长度是3,j2[1]的长度是6,j2[2]的长度是9。

元素的类型和数组的维数是一个数组类型的一部分,但每一维的长度不是。这项不同从语法上看更清楚,每一维的长度是在数组创建表达式中指出而不是在数组类型中。例如下面的声明

int[,,] a3 = new int[10, 20, 30];

为int[,,]类型的数组和一个数组创建表达式new int[10,20,30]。

对于局部变量和域声明,允许一种简写形式,这样就不需要去再声明数组类型。例如,下面的例子

int[] a1 = new int[] {1, 2, 3};

可以简化为

int[] a1 = {1, 2, 3};

程序语意没有任何变化。

一个数组初始化程序的上下文,例如{1,2,3},被用来决定数组被初始化的类型。例子

class Test { static void Main() { short[] a = {1, 2, 3}; int[] b = {1, 2, 3}; long[] c = {1, 2, 3};

} }

说明相同的数组初始化程序可以用于许多不同数组类型的初始化。因为需要上下文来决定一个数组初始化程序的类型,所以不可能在表达式上下文中使用数组初始化程序。

1.2.4 类型系统一致

C# 提供了“统一类型系统”。包括数据类型的所有类型都从object类型派生出来。可以从任何数据调用object的方法,甚至像int这样“简单的”类型的数据。例子

using System;

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 7

C# LANGUAGE REFERENCE

class Test { static void Main() { Console.WriteLine(3.ToString()); } }

用一个整数数据符号来调用在object中定义的ToString方法。

下面这个例子

class Test { static void Main() { int i = 123; object o = i; // boxing int j = (int) o; // unboxing } }

更加有趣。一个int数据可以被转换为object并且还可以转换回int类型。这个例子说明了boxing和unboxing当一个数据类型变量需要转换为引用类型时,一个名为box的对象被分配来保存数据,并且数据被拷贝到box中。Unboxing恰恰相反。当一个对象box要恢复到它原始的数据类型的时候,数据被从对象box中拷贝到适当的存储位置。

类型系统一致提供了有对象性质的数值,而不用引入不需要的开支。在不需要int数值表现得像对象的程序中,int数值只是简单的32位数值。对于需要int数值的行为像一个对象的程序,这项能力是可以实现的。这个能力把数值类型当做大多数语言中存在的数值类型和引用类型间的桥梁。例如类Stack可以提供Push和Pop方法,并返回一个object数值。

public class Stack { public object Pop() {...}

public void Push(object o) {...} }

因为C#有统一类型系统,Stack类可以用来为任何类型数据生成堆栈,包括像int这样的数据类型。

1.3 变量和参数

变量扮演存储的角色。每个变量有一个类型,这个类型决定那些数据可以被存储在这个变量中。局部变量是在方法、属性或索引中声明的变量。一个局部变量通常通过指定的类型名称和说明符来定义,它指定了变量名称和一个任意的初始值,如下:

int a; int b = 1;

但也有可能一个局部变量声明包括多个说明符。对于a和b的声明可以写成:

int a, b = 1;

一个变量在它的数据可以使用前,必须被明确分配数据(§错误!未找到引用源。)。例子 8 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

class Test { static void Main() { int a; int b = 1; int c = a + b; ... } }

是非法的,因为试图在一个变量被分配数据前就试图使用它。

域 (§10.4) 是一种变量,它与某个类或结构或者某个类或结构的实例相关联。一个用static修饰符声明的域定义了一个静态变量,而不用这种修饰符声明的域定义一个实例变量。例子

using System.Data;

class Employee { private static DataSet ds;

public string Name; public decimal Salary;

... }

介绍了Employee类,它有一个私有静态变量和两个公用实例变量。

形式参数声明同样定义变量。这里有四种类型的参数:数据参数,引用参数,输出参数和参量(param)参数。

数据参数用来做“入”参数传递,一个自变量的数据通过它传递到方法中,而对参数的修改不会影响到原始的自变量。数据参数指向它自己在存储器中的位置,它与变量存储位置有明确的区分。次存储位置通过把拷贝相应变量的数据来初始化。例子

using System;

class Test { static void F(int p) { Console.WriteLine("p = {0}", p); p++; }

static void Main() { int a = 1; Console.WriteLine("pre: a = {0}", a); F(a); Console.WriteLine("post: a = {0}", a); } }

说明了一个方法F,它有一个名为p的数据参数。这个例子产生下面的输出:

pre: a = 1 p = 1 post: a = 1

甚至数据p被改动。

引用参数是用作“通过引用”参数传递,这里,参数表现为调用者提供变量的别名。引用参数自己并不定义存储位置,而是指向相应变量的存储位置。对引用参数的修改马上会直接地影响到相应的变量。引用参数用一个ref修饰符来声明。例子

using System;

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 9

C# LANGUAGE REFERENCE

class Test { static void Swap(ref int a, ref int b) { int t = a; a = b; b = t; }

static void Main() { int x = 1; int y = 2; Console.WriteLine("pre: x = {0}, y = {1}", x, y); Swap(ref x, ref y); Console.WriteLine("post: x = {0}, y = {1}", x, y); } }

说明了有两个引用参数的方法Swap。程序的输出如下:

pre: x = 1, y = 2 post: x = 2, y = 1

关键词ref必须在形式参数中声明并且在其中使用。在call位置使用ref要求对参数特殊注意,这样,一个开发人员在阅读此段代码的时候就可以理解到,由于此调用变量将发生变化。

除了调用者所提供变量的初始化数据不重要以外,输出参数与引用参数相似。用一个out修饰符来声明一个输出参数。例子

using System;

class Test { static void Divide(int a, int b, out int result, out int remainder) { result = a / b; remainder = a % b; }

static void Main() { for (int i = 1; i < 10; i++) for (int j = 1; j < 10; j++) { int ans, r; Divide(i, j, out ans, out r); Console.WriteLine("{0} / {1} = {2}r{3}", i, j, ans, r); } } }

介绍了一个包括两个输出参数的Divide方法,一个是除的结果,另外一个是余数。

对于数据,引用和输出参数在调用者提供的变量和代表它们的参数间有一个一一对应的关系。参量参数可以可以允许多对一的关系:多个变量可以由一个参量参数来代表。换句话说参量参数可以接受长度变化的变量列表。

参量参数用一个params修饰符来声明。对于一个给定的方法,只能有一个参量参数,并且通常指定为最后一个参数。参量参数通常是一维数组类型。调用程序可以只是传送这种数组类型的一个单独的变量,也可以是这种数组类型中,于数组元素类型相同的任意多个变量。例如,下面的例子

using System;

10 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

class Test { static void F(params int[] args) { Console.WriteLine("# of arguments: {0}", args.Length); for (int i = 0; i < args.Length; i++) Console.WriteLine("/targs[{0}] = {1}", i, args[i]); }

static void Main() { F(); F(1); F(1, 2); F(1, 2, 3); F(new int[] {1, 2, 3, 4}); } }

介绍了一个F方法,它有可变数量的int变量和许多对这个方法的调用。输出是:

# of arguments: 0 # of arguments: 1 args[0] = 1 # of arguments: 2 args[0] = 1 args[1] = 2 # of arguments: 3 args[0] = 1 args[1] = 2 args[2] = 3 # of arguments: 4 args[0] = 1 args[1] = 2 args[2] = 3 args[3] = 4

在介绍中出现的大部分例子都使用Console类中的Writeline方法。如例子中完全用参量参数进行变量替换。

int a = 1, b = 2; Console.WriteLine("a = {0}, b = {1}", a, b);

WriteLine方法提供了多种传递少量变量的方法,而其中一种使用了参量参数。

namespace System { public class Console { public static void WriteLine(string s) {...}

public static void WriteLine(string s, object a) {...}

public static void WriteLine(string s, object a, object b) {...}

...

public static void WriteLine(string s, params object[] args) {...} } }

1.4 自动内存管理

手工内存管理需要开发者管理内存块的分配和重新分配。手工管理内存既耗时又困难。提供自动内存管理可以使开发者从繁重的任务中解放出来。在大多数情况下,自动内存管理可以在没有反面影响表现和性能的情况下增加代码的质量,提高开发者生产力。 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 11

C# LANGUAGE REFERENCE

例子

using System;

public class Stack { private Node first = null;

public bool Empty { get { return (first == null); } }

public object Pop() { if (first == null) throw new Exception("Can't Pop from an empty Stack."); else { object temp = first.Value; first = first.Next; return temp; } }

public void Push(object o) { first = new Node(o, first); }

class Node { public Node Next;

public object Value;

public Node(object value): this(value, null) {}

public Node(object value, Node next) { Next = next; Value = value; } } }

介绍了一个作为Node实例链接表执行类Stack。Node实例在Push方法中创建,当不再需要时被碎片收集。当其它程序没有任何可能去访问Node实例时,它就符合碎片收集的条件了。例如当一个项目被从Stack中移走,相关的Node实例就变为符合碎片收集的条件。

例子

class Test { static void Main() { Stack s = new Stack();

for (int i = 0; i < 10; i++) s.Push(i);

s = null; } }

介绍了一个使用Stack类的测试程序。Stack被创建并且初始化为包含10个元素,然后被赋值为数据null。一旦变量s被赋值为null后,Stack和相应的10个Node实例就变成符合碎片收集的条件了。碎片收集程序马上就被允许进行清理,虽然还不需要这样做。 12 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

对于一个通常是使用自动内存管理,但有时需要精细控制或希望有些许性能提高的程序员来说,C#提供了编写“非安全”代码的能力。这样的代码可以由指针类型直接处理,而且fix对像可以暂时保护这些代码,防止被碎片收集程序收集。从开发者和用户角度来看,这个“非安全”代码属性实际上是“安全”属性。非安全代码必须用unsafe修饰符明确标明,这样开发者就不会在偶然的情况下使用这项非安全属性,并且编译器和执行程序一起来保证非安全代码不会伪装为安全代码。

例子

using System;

class Test { unsafe static void WriteLocations(byte[] arr) { fixed (byte *p_arr = arr) { byte *p_elem = p_arr; for (int i = 0; i < arr.Length; i++) { byte value = *p_elem; string addr = int.Format((int) p_elem, "X"); Console.WriteLine("arr[{0}] at 0x{1} is {2}", i, addr, value); p_elem++; } } }

static void Main() { byte[] arr = new byte[] {1, 2, 3, 4, 5}; WriteLocations(arr); } }

介绍了名为WriteLocations的非安全方法,它选定一个数组实例并且用指针反复对元素进行操作。每个数组元素的标号,数据和位置写到控制台。这个程序的一个可能的输出为:

arr[0] at 0x8E0360 is 1 arr[1] at 0x8E0361 is 2 arr[2] at 0x8E0362 is 3 arr[3] at 0x8E0363 is 4 arr[4] at 0x8E0364 is 5

但是,准确的存储位置肯定要发生变化。

1.5 表达式

C# 包括一元操作符,二元操作符和一个三元操作符。在下面的表中,对操作符进行了总结,按照从最高到最低的优先顺序列出这些操作符: Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 13

C# LANGUAGE REFERENCE

Section

Category

Operators

0

基本的

(x) x.y f(x) a[x] x++ x-- new

typeof sizeof checked unchecked

错误!未找到引用源。

一元的

+ - ! ~ ++x --x (T)x

错误!未找到引用源。

乘法的

* / %

错误!未找到引用源。

加法的

+ -

错误!未找到引用源。

移位

<< >>

错误!未找到引用源。

关系

< > <= >= is

错误!未找到引用源。

等式

== !=

错误!未找到引用源。

逻辑与

&

错误!未找到引用源。

逻辑异或

^

错误!未找到引用源。

逻辑或

|

错误!

条件与

&&

14 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

未找到引用源。

错误!未找到引用源。

条件或

||

错误!未找到引用源。

条件的

?:

错误!未找到引用源。

赋值

= *= /= %= += -= <<= >>= &= ^= |=

当一个表达式包括几个操作符,操作符的优先级控制顺序,这里对每个单独操作符进行等效。例如,因为*操作符的优先级比+操作符高,所以表达式x+y*z等效为x+(y*z)。

当一个操作数出现在两个有相同优先级的操作符之间时,操作符的传递关系控制操作实现的顺序:

• 除了赋值操作符,所有二元操作符是左向传递,意味着操作是从左向右进行。例如,x+y+z等效为(x+y)+z。

• 赋值操作符和条件操作符是右向传递的,意味着操作是从右向左进行。例如,x=y=z等效为x=(y=z)。

使用括号可以控制优先级和传递关系。例如,x+y*z先把y和z相乘,然后把结果同x相加。但是(x+y)*z首先把x和y相加,然后把结果乘以z。

1.6 声明

虽然有些值得注意的增加和修改,C# 的大多数声明都是从C和C++继承的。下面的表列出了可用的声明的类型,并且为每个提供了例子。 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 15

C# LANGUAGE REFERENCE

声明

例子

声明列表和块声明

static void Main() { F(); G(); { H(); I(); } }

标号声明和goto声明

static void Main(string[] args) { if (args.Length == 0) goto done: Console.WriteLine(args.Length); done: Console.WriteLine("Done"); }

局部常量声明

static void Main() { const float pi = 3.14; const int r = 123; Console.WriteLine(pi * r * r); }

局部变量声明

static void Main() { int a; int b = 2, c = 3; a = 1; Console.WriteLine(a + b + c); }

表达式声明

static int F(int a, int b) { return a + b; }

static void Main() { F(1, 2); // Expression statement }

If声明

static void Main(string[] args) { if (args.Length == 0) Console.WriteLine("No args"); else Console.WriteLine("Args"); }

Switch声明

static void Main(string[] args) { switch (args.Length) { case 0: Console.WriteLine("No args"); break; case 1: Console.WriteLine("One arg "); break; default: int n = args.Length; Console.WriteLine("{0} args", n); break; } }

While声明

static void Main(string[] args) { int i = 0; while (i < args.length) { Console.WriteLine(args[i]); i++; } }

do声明

static void Main() { string s;

16 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

do { s = Console.ReadLine(); } while (s != "Exit"); }

for声明

static void Main(string[] args) { for (int i = 0; i < args.length; i++) Console.WriteLine(args[i]); }

Foreach 声明

static void Main(string[] args) { foreach (string s in args) Console.WriteLine(s); }

Break 声明

static void Main(string[] args) { int i = 0; while (true) { if (i > args.Length) break; Console.WriteLine(args[i++]); } }

continue 声明

static void Main(string[] args) { int i = 0; while (true) { Console.WriteLine(args[i++]); if (i > args.Length) continue; break; } }

return 声明

static int F(int a, int b) { return a + b; }

static void Main() { Console.WriteLine(F(1, 2)); return; }

throw 声明 and try 声明

static int F(int a, int b) { if (b == 0) throw new Exception("Divide by zero"); return a / b; }

static void Main() { try { Console.WriteLine(F(5, 0)); } catch(Exception e) { Console.WriteLine("Error"); } }

checked 和 unchecked 声明

static void Main() { int x = 100000, y = 100000; Console.WriteLine(unchecked(x * y)); Console.WriteLine(checked(x * y)); // Error Console.WriteLine(x * y); // Error }

lock 声明

static void Main() { A a = ... lock(a) { a.P = a.P + 1; } }

问题

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 17

C# LANGUAGE REFERENCE

我们要在这里添加一些章节说明C#与C和C++不同的地方。这些应该是:

• Goto约束

• 不是所有的表达式都可用被用作声明

• if, while, 和 do声明需要二进制表达式

• 对于switch声明没有失败

• foreach 声明

• 例外处理

• Checked 和 unchecked 声明

• Lock 声明

1.7 类

类声明定义新的引用类型。一个类可以从其它类继承,并且可以没用接口或有多个接口。

类的成员可以包括常数、域、方法、属性、索引、事件、操作符、构造函数、析构器和嵌套类型声明。每个成员有相关的访问能力,这控制了可以访问这个成员的程序文本的区域。有访问能力有五种可能形式。在下表中进行总结。

形式

直观意义

public

访问不受限制

protected

访问只限于此程序或类中包含的类型

internal

访问只限于此程序

protected internal

访问只限于此程序或类中包含的类型

private

访问只限于所包含的类型

例子

using System;

class MyClass { public MyClass() { Console.WriteLine("Constructor"); }

public MyClass(int value) { MyField = value; Console.WriteLine("Constructor"); }

~MyClass() { Console.WriteLine("Destructor"); }

public const int MyConstant = 12;

public int MyField = 34;

18 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

public void MyMethod(){ Console.WriteLine("MyClass.MyMethod"); }

public int MyProperty { get { return MyField; }

set { MyField = value; } }

public int this[int index] { get { return 0; }

set { Console.WriteLine("this[{0}] was set to {1}", index, value); } }

public event EventHandler MyEvent;

public static MyClass operator+(MyClass a, MyClass b) { return new MyClass(a.MyField + b.MyField); }

internal class MyNestedClass {} }

介绍了一个包含每种类型成员的类。例子

class Test { static void Main() { // Constructor usage MyClass a = new MyClass(); MyClass b = new MyClass(123);

// Constant usage Console.WriteLine("MyClass.MyConstant = {0}", MyClass.MyConstant);

// Field usage a.MyField++; Console.WriteLine("a.MyField = {0}", a.MyField);

// Method usage a.MyMethod();

// Property usage a.MyProperty++; Console.WriteLine("a.MyProperty = {0}", a.MyProperty);

// Indexer usage a[3] = a[1] = a[2]; Console.WriteLine("a[3] = {0}", a[3]);

// Event usage a.MyEvent += new EventHandler(MyHandler);

// Overloaded operator usage MyClass c = a + b; }

static void MyHandler(object sender, EventArgs e) { Console.WriteLine("Test.MyHandler"); }

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 19

C# LANGUAGE REFERENCE

internal class MyNestedClass {} }

介绍如何使用这些成员。.

1.7.1 常数

一个常数是一个代表常数值的类成员:某个可以在编译时计算的数值。只要没有循环从属关系,允许常数依赖同一程序中的其它常数。例子

class Constants { public const int A = 1; public const int B = A + 1; }

包括一个名为Constants的类,有两个公共常数。

常数是隐式静态类型,可以通过类来访问,

class Test { static void Main() { Console.WriteLine("A = {0}, B = {1}", Constants.A, Constants.B); } }

在此例中打印输出Constants.A和Constants.B的数值。

1.7.2 域

域是一个代表和某对像或类相关的变量的成员。例子

class Color { internal ushort redPart; internal ushort bluePart; internal ushort greenPart;

public Color(ushort red, ushort blue, ushort green) { redPart = red; bluePart = blue; greenPart = green; }

... }

介绍了一个Color类,它有局部的实例域,分别叫做redPart、greenPart和bluePart。域可以是静态的,在下面的例子中

class Color { public static Color Red = new Color(0xFF, 0, 0); public static Color Blue = new Color(0, 0xFF, 0); public static Color Green = new Color(0, 0, 0xFF); public static Color White = new Color(0, 0, 0); public static Color Black = new Color(0xFF, 0xFF, 0xFF); ... }

介绍了Red、Blue、Green、White和Black的静态域。 20 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

静态域在这里并不太合适。当Color类被加载后,域就被初始化了,但是在初始化之后,并不能阻止用户改变它们。而那样的改动可能会引起别的使用Color并认为数值不会变的程序的不可预见的错误。只读域可以用来避免这一错误的发生。对于一个只读域的赋值,只会在相同类中的部分声明和构造函数中发生。这样,就可以通过给静态域添加只读修饰符来增强Color类:

class Color { internal ushort redPart; internal ushort bluePart; internal ushort greenPart;

public Color(ushort red, ushort blue, ushort green) { redPart = red; bluePart = blue; greenPart = green; }

public static readonly Color Red = new Color(0xFF, 0, 0); public static readonly Color Blue = new Color(0, 0xFF, 0); public static readonly Color Green = new Color(0, 0, 0xFF); public static readonly Color White = new Color(0, 0, 0); public static readonly Color Black = new Color(0xFF, 0xFF, 0xFF); }

1.7.3 方法

方法是一个执行可以由对像或类完成的计算或行为的成员。方法有一个形式参数列表(可能为空),一个返回数值(或void),并且可以是静态也可以是非静态。静态方法要通过类来访问。非静态方法,也称为实例方法,通过类的实例来访问。例子

using System;

public class Stack { public static Stack Clone(Stack s) {...}

public static Stack Flip(Stack s) {...}

public object Pop() {...}

public void Push(object o) {...}

public override string ToString() {...}

... }

class Test { static void Main() { Stack s = new Stack(); for (int i = 1; i < 10; i++) s.Push(i);

Stack flipped = Stack.Flip(s);

Stack cloned = Stack.Clone(s);

Console.WriteLine("Original stack: " + s.ToString()); Console.WriteLine("Flipped stack: " + flipped.ToString()); Console.WriteLine("Cloned stack: " + cloned.ToString()); } }

介绍了Stack,它有许多静态方法(Clone和Flip)和许多实例方法(Push、Pop和ToString)。 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 21

C# LANGUAGE REFERENCE

方法可以被重复调用,这意味着只要有一个唯一的签名,多个方法可能有相同的名称。方法的签名包括方法、数据、修饰符和它的形式参数的各种类型的名称。方法的签名不包括return类型。例子

class Test { static void F() { Console.WriteLine("F()"); }

static void F(object o) { Console.WriteLine("F(object)"); }

static void F(int value) { Console.WriteLine("F(int)"); }

static void F(int a, int b) { Console.WriteLine("F(int, int)"); }

static void F(int[] values) { Console.WriteLine("F(int[])"); }

static void Main() { F(); F(1); F((object)1); F(1, 2); F(new int[] {1, 2, 3}); } }

介绍了有一个成员方法F的类。程序的输出为

F() F(int) F(object) F(int, int) F(int[])

1.7.4 属性

属性是提供对对像或类的特性进行访问的成员。属性的例子包括字符串的长度,字体的大小,窗口的焦点,用户的名字,等等。属性是域的自然扩展。两者都是用相关类型成员命名,并且访问域和属性的语法是相同的。然而,与域不同,属性不指示存储位置。作为替代,属性有存取程序,它指定声明的执行来对他们的进行读或写。

属性是由属性声明定义的。属性声明的第一部分看起来和域声明相当相似。第二部分包括一个get存取程序和一个set存取程序。在下面的例子类Button定义了一个Caption属性。

public class Button { private string caption;

public string Caption { get { return caption; } 22 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

set { caption = value; Repaint(); } } }

像Caption属性一样的读写都可以的属性包括get和set存取程序。当属性的值要被读出的时候,会调用get存取程序;当要写属性值的时候,会调用set存取程序。 Properties 在set存取程序中,属性的新值赋给一个名为value的隐含参数。

属性的声明是相对直接了当的,但是属性显式它自己的数值是在使用的时候而不是在声明的时候。可以按照对域进行读写的方法来读写Caption属性:

Button b = new Button();

b.Caption = "ABC"; // set

string s = b.Caption; // get

b.Caption += "DEF”; // get & set

1.7.5 事件

事件是使得对像和类提供通知的成员。一个类通过提供事件声明来定义一个事件,这看起来与域和事件声明相当类似,但是有一个event关键字。这个声明的类型必须是delegate类型。

在这个例子中

public delegate void EventHandler(object sender, Event e);

public class Button { public event EventHandler Click;

public void Reset() { Click = null; } }

Button类定义了一个类型为EventHandler的Click事件。在Button类中,Click成员与一个EventHandler类型的私有域相对应。然而,在Button类外,Click成员只能用在+=和-=操作符的左边。这在添加和删除事件句柄方面限制客户代码。例子

using System;

public class Form1 { public Form1() { // Add Button1_Click as an event handler for Button1’s Click event Button1.Click += new EventHandler(Button1_Click); }

Button Button1 = new Button();

void Button1_Click(object sender, Event e) { Console.WriteLine("Button1 was clicked!"); }

public void Disconnect() { Button1.Click -= new EventHandler(Button1_Click); } }

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 23

C# LANGUAGE REFERENCE

介绍了类Form1,它为Button1的Click事件添加了Button1_Click作为事件句柄。在Disconnect方法中,去掉了事件句柄。

如例子中所示,类Button需要被重写来使用像属性一样的事件声明而不是像域一样的事件声明。

public class Button { public event EventHandler Click { get {...} set {...} }

public void Reset() { Click = null; } }

这个改变不会影响到客户代码,但是因为Click的事件句柄不需要用域来实现,所以允许类Button的执行更灵活。

1.7.6 操作符

操作符是一个定义了可以用来使用在类的实例上的表达式操作符所代表的意义的对象。这里有三种可以定义的操作符:一元操作符,二元操作符和转换操作符。

下面的例子定义了Digit类型,它可以描述0到9间的小数-整数值。

using System;

public struct Digit { byte value;

public Digit(byte value) { if (value < 0 || value > 9) throw new ArgumentException(); this.value = value; }

public Digit(int value): this((byte) value) {}

public static implicit operator byte(Digit d) { return d.value; }

public static explicit operator Digit(byte b) { return new Digit(b); }

public static Digit operator+(Digit a, Digit b) { return new Digit(a.value + b.value); }

public static Digit operator-(Digit a, Digit b) { return new Digit(a.value - b.value); }

public static bool operator==(Digit a, Digit b) { return a.value == b.value; }

public static bool operator!=(Digit a, Digit b) { return a.value != b.value; }

public override bool Equals(object value) { return this == (Digit) value; }

24 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

public override int GetHashCode() { return value.GetHashCode(); }

public override string ToString() { return value.ToString(); } }

class Test { static void Main() { Digit a = (Digit) 5; Digit b = (Digit) 3; Digit plus = a + b; Digit minus = a – b; bool equals = (a == b); Console.WriteLine("{0} + {1} = {2}", a, b, plus); Console.WriteLine("{0} - {1} = {2}", a, b, minus); Console.WriteLine("{0} == {1} = {2}", a, b, equals); } }

Digit类型定义了下面的操作符:

• 从Digit到byte的隐式转换操作符。

• 从byte到Digit的隐式转换操作符

• 把两个Digit数值相加并返回一个Digit数值的加法操作符。

• 把一共Digit数值与其它Digit数值相减,并返回一共digit数值的减法操作符。

• 比较两个digit数值的等式和非等式。

1.7.7 索引

索引(indexer)是使得对象可以像数组一样被索引的成员。然而属性使类似域的访问变得可能,索引使得类似数组的访问变得可能。

作为一个例子,考虑前面给出的类Stack。这个类会需要执行类似数组的访问,所以可能会不通过执行不需要的Push和Pop操作而检查或改变堆栈中的项目。Stack的构造像个列表,但是需要提供方便的数组存取。

索引的声明类似于属性的声明,最大的不同在于索引是无名的(由于this是被索引,所以用于声明中的名称是this)而且索引包含索引参数。索引参数在方括号中提供。例子

using System;

public class Stack { private Node GetNode(int index) { Node temp = first; while (index > 0) { temp = temp.Next; index--; } return temp; }

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 25

C# LANGUAGE REFERENCE

public object this[int index] { get { if (!ValidIndex(index)) throw new Exception("Index out of range."); else return GetNode(index).Value; }

set { if (!ValidIndex(index)) throw new Exception("Index out of range."); else GetNode(index).Value = value; } }

... }

class Test { static void Main() { Stack s = new Stack();

s.Push(1); s.Push(2); s.Push(3);

s[0] = 33; // Changes the top item from 3 to 33 s[1] = 22; // Changes the middle item from 2 to 22 s[2] = 11; // Changes the bottom item from 1 to 11 } }

介绍了一个Stack中的索引

1.7.8 实例构造函数

实例构造函数是实现对类中实例进行初始化的行为的成员。

例子

using System;

class Point { public double x, y;

public Point() { this.x = 0; this.y = 0; }

public Point(double x, double y) { this.x = x; this.y = y; }

public static double Distance(Point a, Point b) { double xdiff = a.x – b.x; double ydiff = a.y – b.y; return Math.Sqrt(xdiff * xdiff + ydiff * ydiff); }

public override string ToString() { return string.Format("({0}, {1})", x, y); } }

26 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

class Test { static void Main() { Point a = new Point(); Point b = new Point(3, 4); double d = Point.Distance(a, b); Console.WriteLine("Distance from {0} to {1} is {2}", a, b, d); } }

介绍了一个类Point,它提供了两个公用的构造函数。一个没有参数的Point构造函数和一个有两个double参数的构造函数。

如果类中没有提供构造函数,那么会自动提供一个没有参数的构造函数。

1.7.9 析构函数

析构函数是实现破坏一个类的实例的行为的成员。析构函数不能有参数,不能任何修饰符而且不能被调用。析构函数在碎片收集时会被自动调用。

例子

using System;

class Point { public double x, y;

public Point(double x, double y) { this.x = x; this.y = y; }

~Point() { Console.WriteLine("Destructed {0}", this); }

public override string ToString() { return string.Format("({0}, {1})", x, y); } }

介绍了一个有析构函数的类Point。

1.7.10 静态构造函数

静态构造函数是实现对一个类进行初始化的行为的成员。静态构造函数不能有参数,不能有修饰符而且不能被调用,当类被加载时,类的静态构造函数自动被调用。

例子

using System.Data;

class Employee { private static DataSet ds;

static Employee() { ds = new DataSet(...); }

public string Name; public decimal Salary;

... }

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 27

C# LANGUAGE REFERENCE

介绍了一个有静态构造函数的类Employee,这个函数对静态域进行初始化。

1.7.11 继承

类支持单继承,object类型是所有类的基类。

前面所介绍的例子中的类都是隐含地从object派生而来的。例子

class A { public void F() { Console.WriteLine("A.F"); } }

介绍了从object派生出来的类A。例子

class B: A { public void G() { Console.WriteLine("B.G"); } }

class Test { static void Main() { B b = new B(); b.F(); // Inherited from A b.G(); // Introduced in B

A a = b; // Treat a B as an A a.F(); } }

介绍了从类A中派生出来的类B。类B继承了类A的方法F,并且创建了自己的方法G。

方法,属性和索引都可以是虚的,这意味着他们可以在派生的类中被重写。例子

using System;

class A { public virtual void F() { Console.WriteLine("A.F"); } }

class B: A { public override void F() { base.F(); Console.WriteLine("B.F"); } }

class Test { static void Main() { B b = new B(); b.F();

A a = b; a.F(); } }

介绍了有虚方法F的类A,而类B替换了F。B中的替换方法包含了一个对A中被替换的方法的调用base.F()。 28 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

可以通过包括abstract修饰符来说明一个类是不完整的,只是用作其它类的基类。这样的类被称为抽象类。抽象类可以指定抽象函数-非抽象派生类必须实现的成员。例子

using System;

abstract class A { public abstract F(); }

class B: A { public override F() { Console.WriteLine("B.F"); } }

class Test { static void Main() { B b = new B(); B.F();

A a = b; a.F(); } }

介绍了抽象类A中的抽象类F,非抽象类B提供了对此方法的实现。

1.8 结构

类和结构的相似之处有很多-结构可以实现接口,并且可以有同类一样的成员。结构和类在很多重要的方面也不相同,无论如何:结构是数值类型而不是引用类型,而且结构不支持继承。结构的数值或是存储“在堆栈中”或是“在线”。仔细的程序员可以通过明智地使用结构来提高性能。

例如,对Point使用结构而不是类,会在成员的存储位置上造成很大不同。下面的程序创建并初始化了一个100个单元的数组。如果Point用类实现,程序就会占用101个分立对象,一个分给数组另外100个分给每个元素。

class Point { public int x, y;

public Point(int x, int y) { this.x = x; this.y = y; } }

class Test { static void Main() { Point[] points = new Point[100]; for (int i = 0; i < 100; i++) points[i] = new Point(i, i*i); } }

如果Point用结构来代替,

struct Point { public int x, y;

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 29

C# LANGUAGE REFERENCE

public Point(int x, int y) { this.x = x; this.y = y; } }

这样测试程序只会占用一个对象,代表的数组。Point实例在数组内在线分配。这种优化可能会被错误使用。由于当作为数值参数传输一个结构实例时会造成要创建结构,因此用结构替代类也许会使程序变得缓慢和庞大。小心的数据结构和算法设计是无可替代的。

1.9 接口

接口定义了一个连接。一个类或这结构必须根据它的连接来实现接口。接口可以把方法、属性、索引和事件作为成员。

例子

interface IExample { string this[int index] { get; set; }

event EventHandler E;

void F(int value);

string P { get; set; } }

public delegate void EventHandler(object sender, Event e);

介绍了一个包括一个索引、一个事件E、一个方法F和一个属性P。

接口可以使用多个继承。在例子中

interface IControl { void Paint(); }

interface ITextBox: IControl { void SetText(string text); }

interface IListBox: IControl { void SetItems(string[] items); }

interface IComboBox: ITextBox, IListBox {}

接口IcomboBox继承了ItextBox和IlistBox。

类和结构可以实现多个接口。在例子中

interface IDataBound { void Bind(Binder b); }

public class EditBox: Control, IControl, IDataBound { public void Paint();

public void Bind(Binder b) {...} }

30 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

类EditBox从类Control中派生并且实现了Icontrol和IdataBound。

在前面的例子中接口Icontrol中的Paint方法和IdataBound接口中的Bind方法都用类EditBox中的公共成员实现。C#提供一种实现这些方法的可选择的途径,这样可以使执行这些的类避免把这些成员设定为公共的。接口成员可以用有效的名称来实现。例如,类EditBox可以改作方法Icontrol.Paint和IdataBound.Bind来来实现。

public class EditBox: IControl, IDataBound { void IControl.Paint() {...}

void IDataBound.Bind(Binder b) {...} }

因为通过外部指派接口成员实现了每个成员,所以用这种方法实现的成员称为外部接口成员。外部接口成员可以只是通过接口来调用。例如,Paint方法中EditBox的实现可以只是通过创建Icontrol接口来调用。

class Test { static void Main() { EditBox editbox = new EditBox(); editbox.Paint(); // error: EditBox does not have a Paint method

IControl control = editbox; control.Paint(); // calls EditBox’s implementation of Paint } }

1.10 代表

因为C++和一些其它语言可以用函数指针来进行访问,所以代表(delegates)使得这一特定情况变得可能。与函数指针不同,代表是面向对象的,类型安全并且是可靠的。

代表是引用类型,它从公共基类:System.Delegate派生出来。一个代表实例压缩了一个方法-一个可调用的实体。对于静态方法,一个可调用实体由类和类中的静态方法组成。

代表的一个有趣而且有用的特性是它不知道或这不关心与它相关的对象的类型。对象所要做的所有事情是方法的签名和代表的签名相匹配。这使得代表很适合“匿名”调用。这是个很有用的功能。

定义和使用代表分为三步:声明、实例化和调用。用delegate声明语法来声明代表。例子

delegate void SimpleDelegate();

声明了一个名为SimpleDelegate的代表,它没有任何参数并且返回void值。

例子

class Test { static void F() { System.Console.WriteLine("Test.F"); }

static void Main() { SimpleDelegate d = new SimpleDelegate(F); d(); } }

创建了一个SimpleDelegate实例,并且马上就对它进行调用。 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 31

C# LANGUAGE REFERENCE

对于为一个方法实例化一个代表并且马上调用它没有什么好说的,理由是它会比直接调用方法要简单得多。代表在匿名使用时会显式它的用处。例子

void MultiCall(SimpleDelegate d, int count) { for (int i = 0; i < count; i++) d(); } }

介绍了一个方法MultiCall,它重复调用SimpleDelegate。方法MultiCall并不知道或这关心哪种类型方法是SimpleDelegate的目标方法,这种方法有什么样的可达性,或这些方法是静态的还是非静态的。所有关心的事情是目标方法的签名是否与一致。

1.11 联合

联合类型的声明为一个符号常数相关的组定义了一个类型名称。联合应用于“多选择”的场合,这里运行时间是由在编译时所知道的固定的选择数目所决定。

例子

enum Color { Red, Blue, Green }

class Shape { public void Fill(Color color) { switch(color) { case Color.Red: ... break;

case Color.Blue: ... break;

case Color.Green: ... break;

default: break; } } }

介绍了一个联合color和一个使用这个联合的方法。方法Fill的名称使人很容易明白形状可以用所给的颜色中的一种进行填充。

因为使用联合可以使的代码更可读还可以自归档,所以使用联合比使用整数常数要好-当然,很多语言中通常没有联合。代码的自归档特点也使得开发工具可以帮助编写代码的和进行一些其它的“设计者”行为。例如,使用Color而不是int作为参数类型,使得精确代码编辑器可以给出Color的数值。

1.12 名称空间和集合

迄今为止所提供的程序除了依靠一些系统提供的如System.Console类以外,都是基于自身的。而对于实际程序来说,由许多不同的片断组成是很普通的。例如,一个完整的程序也许是基于几个不同的成分,包括一些内部开发的一些独立软件商处购买的程序。 32 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

名称空间(Namespaces)和集合使得这个基于成分的系统成为可能。 名称空间提供了一个逻辑组织系统。名称空间既可以用作一个程序的“内部”组织系统,也可以用作“外部”组织系统。外部组织是一种使用其它程序提供的公开程序元素的方法。

集合Assemblies are用于物理包装和配置。集合表现得像一个类型的容器。一个集合 中可以包含类型、用于实现这些类型的可执行代码和对于其它集合的连接。

这里有两种主要的集合种类:应用程序和库。应用程序有一个主入口,并且通常使用文件扩展名.exe;库没有主入口点,并且通常使用文件扩展名.dll。

为了介绍名称空间和集合的使用,节中我们再看一看前面给出的程序“Hello,world”,并且把它分为两部分:一个提供消息和信息的库和一个独立的显式它们的应用程序。

库中将包含一个简单的类名为HelloMessage。例子

// HelloLibrary.cs

namespace Microsoft.CSharp.Introduction { public class HelloMessage { public string Message { get { return "Hello, world"; } } } }

用一个名称空间Microsoft.Csharp.Introduction来为类HelloMessage命名。类HelloMessage提供了一个只读属性名为Message。名称空间可以嵌套,而声明

namespace Microsoft.CSharp.Introduction {...}

是对多级名称空间 嵌套的简写:

namespace Microsoft { namespace CSharp { namespace Introduction {....} } }

“Hello,world”程序各部分的下一步是用类HelloMessage写一个控制台应用程序。这个类的全称为Microsoft.CSharp.Introduction.HelloMessage ,它要被使用,但是这个名字太长而且很笨拙。一个简单一些的方法是使用名称空间指示,这使得它可以无条件使用所有名称空间中的类型。例子

// HelloApp.cs

using Microsoft.CSharp.Introduction;

class HelloApp { static void Main() { HelloMessage m = new HelloMessage(); System.Console.WriteLine(m.Message); } }

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 33

C# LANGUAGE REFERENCE

介绍了指向Microsoft.CSharp.Introduction名称空间的名称空间指示。HelloMessage 所发生的事情是对Microsoft.CSharp.Introduction.HelloMessage进行了简写。

C#也使使用别名进行定义成为可能。使用别名指示为一个类型定义别名。这样的别名在两个库中的名称发生冲突或使用相当大的名称空间中的小部分类型时,就显得很有用了。例子

using MessageSource = Microsoft.CSharp.Introduction.HelloMessage;

介绍了使用别名指示把MessageSoure定义为类HelloMessage的别名。

我们所写的代码可以被编译为一个包含了类HelloMessage的库和一个包含了类HelloApp的应用程序。编译的具体过程是根据所使用的编译器会有所不同。用visual Studio 7.0提供的命令行编译器,正确的使用为:

csc /target:library HelloLibrary.cs

它生成类库HelloLibrary.dll 和

csc /reference:HelloLibrary.dll HelloApp.cs

它生成应用程序HelloApp.exe.

1.13 版本

版本Versioning is是使得组分随着时间流逝保持一致的方法。如果基于先前版本的代码在编译后可以同新版本一起工作,那么说这一部分的新版本与以前的版本是代码兼容的。与此相同,如果一个基于旧版本的程序没有经过再编译就可以同新版本程序一起工作,那么说这部分的新版本和以前版本是二进制兼容。

大多数语言根本不支持二进制兼容,而还有许多在是代码兼容变得容易方面所做甚少。实际上,通常是一些语言有缺陷,这使得不破坏至少是某些客户代码而使类随时间更新变得不可能。

作为一个例子,想像这样一个情况,一个基类编写者编写了一个名为Base的类。在最初的版本,Base不包括方法F。从Base中派生出一个名为Derived的部件,并且引入了F。这个派生的类和它依赖的类Base一起发放给用户,配置到无数客户端和服务器上。

// Author A namespace A { class Base // version 1 { } }

// Author B namespace B { class Derived: A.Base { public virtual void F() { System.Console.WriteLine("Derived.F"); } } }

迄今为止,很好。但是现在开始出现版本问题了。Base的作者制作了一个新版本,并且添加了他自己的方法F。 34 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

// Author A namespace A { class Base // version 2 { public virtual void F() { // added in version 2 System.Console.WriteLine("Base.F"); } } }

新版本的Base应该从源代码和二进制都与最初的版本兼容。(如果连简单的添加一个方法都不行,那么基类就不能更新了。)不幸的是,类Base中的新F使得Derived中的F变得意义不明。难道Derived要覆盖Base中的F?这看起来不像,在Derived被编译的时候,Base甚至还没有一个F!此外,如果Derived的F把Base中的F覆盖掉,那么它必须满足由Base确定的协定,一项在Derived被编写时不确定的协议?在某些情况下,这是可能的。例如Base中也许要被覆盖的F的约定经常调用基类。Derived中的F不可能依据这样一个约定。

C# 通过要求开发者明确他们的意图来处理这种版本问题。在前面的代码实例化中,由于Base中甚至没有F,因此代码是清楚的。很显然,由于不存在名为F的基本法,因此Derived中的F被认做是一个新方法而不是对一个基类方法的覆盖。

// Author A namespace A { class Base { } }

// Author B namespace B { class Derived: A.Base { public virtual void F() { System.Console.WriteLine("Derived.F"); } } }

如果Base增加了F,而且变为一个新版本,那么Derived的二进制版本的意图仍然清楚,Derived中的F语意不相关,不能被看做是一个替代。

然而,当Derived被重新编译,意义就不 清楚了。Derived的作者也许会趋向于用他的F覆盖掉Base的F,或者把它隐藏起来。由于意图不清楚,编译器会产生一个警告,而且默认做法是用Derived的F把Base中的F隐藏起来。这个行为过程重复了在Derived不被再编译的语意。这个警告只是用来告诉Derived的作者Base中的方法F的存在。

如果Derived的F从语意上与Base中的F无关,那么Derived的作者可以表达这个意图,并且,实际上可以关掉警告,在F的声明中使用new关键字。 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 35

C# LANGUAGE REFERENCE

// Author A namespace A { class Base // version 2 { public virtual void F() { // added in version 2 System.Console.WriteLine("Base.F"); } } }

// Author B namespace B { class Derived: A.Base // version 2a: new { new public virtual void F() { System.Console.WriteLine("Derived.F"); } } }

另外,Derived的作者应该考虑将来,并且决定Derived中的F是否应该替代Base中的。这样的意图可以通过使用override关键字来表示,如下所示。

// Author A namespace A { class Base // version 2 { public virtual void F() { // added in version 2 System.Console.WriteLine("Base.F"); } } }

// Author B namespace B { class Derived: A.Base // version 2b: override { public override void F() { base.F(); System.Console.WriteLine("Derived.F"); } } }

Derivied的作者还有其它的选择,就是改变F的名称,这样就会完全避免名称的冲突。虽然这样的改动会破坏Derived的源码和二进制兼容性,但是兼容性的重要性是根据情况变化的。如果Derived不提供给其它程序,那么改变F的名称是一个不错的主意,它将增强程序的可读性,并且将不会有任何关于F的意义的冲突。

1.14 特征

C# 是一个程序上的语言,所以同所有程序上的语言相似,它有一些说明的元素。例如,一个类中一个方法的访问能力可以通过修饰符public、protected、internal、protected internal和private来区分。因为它支持特征,所以程序员可以发明出新的声明信息,为各种各样的程序实体指定声明信息,并且在运行时找回这些声明信息。程序通过使用特征来指定这个附加的声明信息。 36 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

例如,一个框架可以定义HelpAttribute特征,它可以放在程序程序元素如类和方法,使得开发者可以从程序元素中提供标记来对它们进行归档。例子

[AttributeUsage(AttributeTargets.All)] public class HelpAttribute: System.Attribute { public HelpAttribute(string url) { this.url = url; }

public string Topic = null;

private string url;

public string Url { get { return url; } } }

定义了一个名为HelpAttribute的特征类(或者简称为Help),它有一个位置参数(字符串url)和一个命名的数组(字符串Topic)。位置参数用特征类中公共构造函数的形式参数定义,而命名参数用特征类的公共读写属性来定义。

例子

[Help("http://www.mycompany.com/…/Class1.htm")] public class Class1 { [Help("http://www.mycompany.com/…/Class1.htm", Topic = "F")] public void F() {} }

介绍了许多对特征的使用。

对于一个给定程序元素的特征信息可以在运行时通过使用反射支持找回。例子

using System;

class Test { static void Main() { Type type = typeof(Class1); object[] arr = type.GetCustomAttributes(typeof(HelpAttribute)); if (arr.Length == 0) Console.WriteLine("Class1 has no Help attribute."); else { HelpAttribute ha = (HelpAttribute) arr[0]; Console.WriteLine("Url = {0}, Topic = {1}", ha.Url, ha.Topic); } } }

察看Class1是否有Help特征,并且如果特征存在,则写出相关的Topic和Url数值。 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 37

 

Chapter 错误!未定义样式。 错误!未定义样式。

2. 语法结构

2.1 翻译的阶段

一个C#程序由一个或多个源文件组成。一个源文件是一个统一字符编码的字符的有序序列。源文件通常和文件系统中的文件有一一对应关系,但是这个对应关系并不需要。

从概念来讲,一个程序在编译时有四步:

1. 予处理,一种文本到文本的转换,这使得可以对程序文本进行条件包含和删除。

2. 语法分析,它把输入字符序列转换为一个标记序列。

3. 句法分析,它把标记序列转换为可执行代码。

2.2 文法符号

C#的词汇和句子的文法散布在整个文章中。词汇文法定义如能把字符组合为形式标记;句子的文法定义了如何把标记组合为C#程序。

文法生成包括无词尾符号和有词尾符号。在文法生成当中,无词尾符号用意大利体表示,而有词尾符号用定宽字体。每一个无词尾符号定义为一系列产品(production)。这一系列产品的第一行是无词尾符号的名称,接下来是一个冒号。对于一个产品,每个连续的锯齿状的行的右手边同左手边类似是无词尾符号。例子:

nonsense: terminal1 terminal2

定义了一个名为 nonsense 的无词尾符号,有两个产品,一个在右手边是terminal1,一个在左手边是terminal2。

选项通常列为单独的一行,虽然有时有很多选项,短语“one of”会在选项前面。这里有一个对把每个选项都列在单独一行的简单缩写的方法。例子

letter: one of A B C a b c

简写为:

letter: one of A B C a b c

如identifieropt ,一个写在下方的前缀 “opt”用来作为简写来指明一个可选的符号。例子

whole: first-part second-partopt last-part

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 39

C# LANGUAGE REFERENCE

是下面的缩写:

whole: first-part last-part first-part second-part last-part

2.3 预处理

预阶段是一个文本到文本的转换阶段,在预处理过程中,使能进行代码的条件包含和排除。

pp-unit: pp-groupopt

pp-group: pp-group-part pp-group pp-group-part

pp-group-part: pp-tokensopt new-line pp-declaration pp-if-section pp-control-line pp-line-number

pp-tokens: pp-token pp-tokens pp-token

pp-token: identifier keyword literal operator-or-punctuator

new-line: The carriage return character (U+000D) The line feed character (U+000A) The carriage return character followed by a line feed character The line separator character (U+2028) The paragraph separator character (U+2029)

2.3.1 预处理声明

在预处理过程中,为了使用名称可以被定义和取消定义。#define定义一个标识符。#undef“反定义”一个标识符,如果一个标识符在以前已经被定义了,那么它就变成了不明确的。如果一个标识符已经被定义了,它的语意就等同于true;如果一个标识符没有意义,那么它的语意等同于false。

pp-declaration: #define pp-identifier #undef pp-identifier

例子:

#define A #undef B

class C {

40 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

#if A void F() {} #else void G() {} #endif

#if B void H() {} #else void I() {} #endif }

变为:

class C { void F() {} void I() {} }

如果有一个 pp-unit, 声明就必须用pp-token元素进行。换句话说,#define和#undef必须在文件中任何“真正代码”前声明,否则在编译时会发生错误。因此,也许会像下面的例子一样散布#if和#define:

#define A #if A #define B #endif namespace N { #if B class Class1 {} #endif }

因为#define放在了真实代码后面,所以下面的例子是非法的:

#define A namespace N { #define B #if B class Class1 {} #endif }

一个#undef也许会“反定义”一个没有定义的名称。下面的例子中定义了一个名字并且对它进行了两次反定义,第二个#undef没有效果,但还是合法的。

#define A #undef A #undef A

2.3.2 #if, #elif, #else, #endif

pp-if-section 用来对程序文本的一部件进行有条件地包括和排除。

pp-if-section: pp-if-group pp-elif-groupsopt pp-else-groupopt pp-endif-line

pp-if-group: #if pp-expression new-line pp-groupopt

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 41

C# LANGUAGE REFERENCE

pp-elif-groups pp-elif-group pp-elif-groups pp-elif-group

pp-elif-group: #elif pp-expression new-line groupopt

pp-else-group: #else new-line groupopt

pp-endif-line #endif new-line

例子:

#define Debug

class Class1 { #if Debug void Trace(string s) {} #endif }

变成:

class Class1 { void Trace(string s) {} }

如果这部分可以嵌套。例子:

#define Debug // Debugging on #undef Trace // Tracing off

class PurchaseTransaction { void Commit() { #if Debug CheckConsistency(); #if Trace WriteToLog(this.ToString()); #endif #endif CommitHelper(); } }

2.3.3 预处理控制行

特性#error 和 #warning 使得代码可以把警告和错误的条件报告给编译程序,来查出标准的编译时的 警告和错误。

pp-control-line: #error pp-message #warning pp-message

pp-message: pp-tokensopt

例子

#warning Code review needed before check-in

42 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

#define DEBUG

#if DEBUG && RETAIL #error A build can't be both debug and retail! #endif

class Class1 {…}

总是产生警告(“Code review needed before check-in"),并且如果予处理修饰符DEBUG和RETAIL都被定义,还会产生错误。

2.3.4 #line

#line的特点使得开发者可以改变行的数量和编译器输出时使用的源文件名称,例如警告和错误。如果没有行指示符,那么行的数量和文件名称就会自动由编译器定义。#line指示符通常用于编程后的工具 ,它从其它文本输入产生C#源代码。

pp-line-number: #line integer-literal #line integer-literal string-literal

pp-integer-literal: decimal-digit decimal-digits decimal-digit

pp-string-literal: " pp-string-literal-characters "

pp-string-literal-characters: pp-string-literal-character pp-string-literal-characters pp-string-literal-character

pp-string-literal-character: Any character except " (U+0022), and white-space

2.3.5 预处理标识符

预处理标识符使用和规则C#标识符文法相似的文法:

pp-identifier: pp-available-identifier

pp-available-identifier: A pp-identifier-or-keyword that is not true or false

pp-identifier-or-keyword: identifier-start-character identifier-part-charactersopt

true和false符号不是合法的预定义指示符,所以不能用于#define的定义和#undef的反定义。

2.3.6 预处理表达式

操作符 !, ==, !=, && 和 ||是允许的预定义表达式。在预定义表达式中,圆括号可以用来分组。

pp-expression: pp-equality-expression

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 43

C# LANGUAGE REFERENCE

pp-primary-expression: true false pp-identifier ( pp-expression )

pp-unary-expression: pp-primary-expression ! pp-unary-expression

pp-equality-expression: pp-equality-expression == pp-logical-and-expression pp-equality-expression != pp-logical-and-expression

pp-logical-and-expression: pp-unary-expression pp-logical-and-expression && pp-unary-expression

pp-logical-or-expression: pp-logical-and-expression pp-logical-or-expression || pp-logical-and-expression

2.3.7 与空白交互作用

条件编译标识符必须在一行的第一个非空白位置。

一个单行注释可以跟在条件编译指示符或pp-control-line 标识符后面。例如:

#define Debug // Defined if the build is a debug build

对于pp-control-line 标识符,一行的剩余组成pp-message,独立于此行的注释。例子:

#warning // TODO: Add a better warning

会有一个注释为"// TODO: Add a better warning"的警告。

一个多行注释的起始和结束可以不在同一行中,就像条件编译标识符。例子

/* This comment is illegal because it ends on the same line*/ #define Debug

/* This is comment is illegal because it is on the same line */ #define Retail

#define A /* This is comment is illegal because it is on the same line */

#define B /* This comment is illegal because it starts on the same line */

结果将是编译时错误。

可以形成一个条件编译标识符的数据符号可能会隐含在注释中。例子

// This entire line is a commment. #define Debug

/* This text would be a cc directive but it is commented out: #define Retail */

不包含任何条件编译标识符,然而完全由空白组成。 44 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

2.4 语法分析

语法分析阶段把输入字符流转换为标记流。

2.4.1 输入

input: input-elementsopt

input-elements: input-element input-elements input-element

input-element: comment white-space token

2.4.2 输入字符

input-character: any Unicode character

2.4.3 行结束符

line-terminator: The carriage return character (U+000D) The line feed character (U+000A) The carriage return character followed by a line feed character The line separator character (U+2028) The paragraph separator character (U+2029)

2.4.4 注释

支持两种形式的注释:规则注释和单行注释。

规则注释以/*开始,并且以*/结束。规则注释可以占用一行的一部分,单行或多行。例子

/* Hello, world program This program writes “Hello, world” to the console */ class Hello { static void Main() { Console.WriteLine("Hello, world"); } }

包括多个规则注释。

单行注释开始与字符// 并且延伸到行的结束。例子 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 45

C# LANGUAGE REFERENCE

// Hello, world program // This program writes “Hello, world” to the console // class Hello // any name will do for this class { static void Main() { // this method must be named "Main" Console.WriteLine("Hello, world"); } }

介绍了多个单行注释。

comment: regular-comment one-line-comment

regular-comment: / * rest-of-regular-comment

rest-of-regular-comment: * rest-of-regular-comment-star not-star rest-of-regular-comment

rest-of-regular-comment-star: /* rest-of-regular-comment-star not-star-or-slash rest-of-regular-comment

not-star: Any input-character except *

not-star-or-slash: Any input-character except * and /

one-line-comment: / / one-line-comment-text line-terminator

one-line-comment-text: input-character one-line-comment-text input-character

例子:

// This is a comment int i;

/* This is a multiline comment */ int j;

2.4.5 空白

white-space: new-line The tab character (U+0009) The vertical tab character (U+000B) The form feed character (U+000C) The "control-Z" or "substitute" character (U+001A) All characters with Unicode class "Zs"

46 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

2.4.6 标记

这里有五种标记:标识符、关键字、数据符号、操作符和标点。因为空白像是标记的分割符,所以被忽略了。

token: identifier keyword literal operator-or-punctuator

2.5 句法分析

句法分析阶段把标记流转换为可执行代码。

2.5.1 标识符

标识符的规则符合统一字符编码标准3.0,除了下划线允许使用起首大写字母,格式化字符(类Cf)不允许用于标识符而统一字符编码标准中的escape字符允许用在标识符中。

identifier: available-identifier @ identifier-or-keyword

available-identifier: An identifier-or-keyword that is not a keyword

identifier-or-keyword: identifier-start-character identifier-part-charactersopt

identifier-start-character: letter-character underscore-character

identifier-part-characters: identifier-part-character identifier-part-characters identifier-part-character

identifier-part-character: letter-character combining-character decimal-digit-character underscore-character

letter-character: A Unicode character of classes Lu, Ll, Lt, Lm, Lo, or Nl A unicode-character-escape-sequence representing a character of classes Lu, Ll, Lt, Lm, Lo, or Nl

combining-character: A Unicode character of classes Mn or Mc A unicode-character-escape-sequence representing a character of classes Mn or Mcdecimal-digit-character: A Unicode character of the class Nd A unicode-character-escape-sequence representing a character of the class Nd Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 47

C# LANGUAGE REFERENCE

underscore-character: A Unicode character of the class Pc A unicode-character-escape-sequence representing a character of the class Pc

合法标识符的例子包括“identifier1”, “_identifier2”, 和 “@if”。

前缀“@”使得可以在标识符中使用关键词。实际上字符@不是标识符的一部分,所以如果没有这个前缀,可能在另外一种语言中被视为通常的标识符 。不是关键字也在标识符中使用前缀@是允许的,但是这是一种很不好的风格。

例子

class @class { static void @static(bool @bool) { if (@bool) Console.WriteLine("true"); else Console.WriteLine("false"); } }

class Class1 { static void M { @class.@static(true); } }

定义了一个名为“class”的类,有一个静态方法名为“static”,他使用了一个名为“bool”的参数。

2.5.2 关键字

关键字是类似于标识符的保留字符序列,除非用@字符开头,否则不能用作标识符。

keyword: one of abstract base bool break byte case catch char checked class const continue decimal default delegate do double else enum event explicit extern false finally fixed float for foreach goto if implicit in int interface internal is lock long namespace new null object operator out override params private protected public readonly ref return sbyte sealed short sizeof static string struct switch this throw true try typeof uint ulong unchecked unsafe ushort using virtual void while

2.5.3 数据符号

数据符号是数值的源代码表示。 48 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

literal: boolean-literal integer-literal real-literal character-literal string-literal null-literal

2.5.3.1 二进制数据符号

这里有两种二进制数值:true和false。

boolean-literal: true false

2.5.3.2 整数数据符号

整数数据符号有两种可能的形式:十进制和十六进制。

integer-literal: decimal-integer-literal hexadecimal-integer-literal

decimal-integer-literal: decimal-digits integer-type-suffixopt

decimal-digits: decimal-digit decimal-digits decimal-digit

decimal-digit: one of 0 1 2 3 4 5 6 7 8 9

integer-type-suffix: one of U u L l UL Ul uL ul LU Lu lU lu

hexadecimal-integer-literal: 0x hex-digits integer-type-suffixopt

hex-digits: hex-digit hex-digits hex-digit

hex-digit: one of 0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f

整数数据符号的类型按下面确定:

• 如果数据符号没有后缀,它就有这些类型中的第一个,这些类型可以表示出它的数值:int、uint、long、ulong。

• 如果数据符号有后缀U或u,它就有这些类型中的第一个,这些类型可以表示出它的数值: uint, ulong。

• 如果数据符号有后缀L或l,If the literal is suffixed by L or l, 它就有这些类型中的第一个,这些类型可以表示出它的数值: long, ulong。 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 49

C# LANGUAGE REFERENCE

• 如果数据符号有后缀UL、 Ul、 uL、 ul、 LU、 Lu、 lU 或 lu,它的类型就是ulong。

如果用整数数据符号表示的数值超出了ulong类型,就会产生错误。

为了允许最小的可能的int和long数值可以用十进制整数描述,存在下面两个规则:

• 当一个十进制整数数据符号的数值为2147483648 (231),并且没有整数类型后缀,而操作数有一元负操作符(§错误!未找到引用源。)时 ,结果是int类型的常数,数值为−2147483648 (−231)。在所有其它情况下,这样的一个十进制整数数据符号是uint类型。

• 当一个十进制整数数据符号的数值为9223372036854775808 (263),并且没有整数类型后缀或整数类型后缀L或l,而操作数有一元负操作符(§错误!未找到引用源。)时 ,结果是long类型的常数,数值为−9223372036854775808 (−263)。在所有其它情况下,这样的一个十进制整数数据符号是ulong类型。

2.5.3.3 实数据符号

real-literal: decimal-digits . decimal-digits exponent-partopt real-type-suffixopt . decimal-digits exponent-partopt real-type-suffixopt decimal-digits exponent-part real-type-suffixopt decimal-digits real-type-suffix

exponent-part: e signopt decimal-digits E signopt decimal-digits

sign: one of + -

real-type-suffix: one of F f D d M m

如果没有指定real类型后缀,实数据符号的类型是double。否则,实类型后缀决定了实数据符号,如下:

• 一个实数据以F或f为后缀是float类型。例如数据符号1f、 1.5f、 1e10f、和 −123.456F 都是float类型数据。

• 一个实数据以D或d为后缀是double类型。例如数据符号1d、 1.5d、 1e10d、和 −123.456d 都是double类型数据。

• 一个实数据以M或m为后缀是decimal类型。例如数据符号1m、 1.5m、 1e10m、和 −123.456m 都是decimal类型数据。

如果所指定的数据符号不能用指定类型表示,在编译时会产生错误。

2.5.3.4 字符数据符号

字符数据符号是一个用号括起来的单个字符,如'a'。

character-literal: ' character '

50 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

character: single-character simple-escape-sequence hexadecimal-escape-sequence unicode-character-escape-sequence

single-character: Any character except ' (U+0027), / (U+005C), and new-line

simple-escape-sequence: one of /' /" // /0 /a /b /f /n /r /t /v

hexadecimal-escape-sequence: /x hex-digit hex-digitopt hex-digitopt hex-digitopt

在一个单转意符序列或一个十六进制转意符序列中,一个跟在反斜杠字符(/)后面的字符必然是下面的字符之一: '、 "、 /、 0、 a、 b、 f、 n、 r、 t、 x、 v。否则,在编译是会发生错误。

一个简单的转意符序列表示了统一的字符编码的字符编码,如下表所示。

转意序列

字符名称

Unicode 编码

/'

Single quote

0x0027

/"

Double quote

0x0022

//

Backslash

0x005C

/0

Null

0x0000

/a

Alert

0x0007

/b

Backspace

0x0008

/f

Form feed

0x000C

/n

New line

0x000A

/r

Carriage return

0x000D

/t

Horizontal tab

0x0009

/v

Vertical tab

0x000B

2.5.3.5 字符串数据符号

C# 支持两种形式的字符串数据符号:规则字符串数据符号和逐字的字符串数据符号。规则字符串数字符号由用双引号括起0或更多字符组成,例如"Hello, world",并且也许会包括简单转意序列(例如/t表示tab字符)和十六进制转意序列。

逐字的字符串数据符号由一个@字符后面跟着双引号括起的0或者更多字符组成。一个简单的例子是@"Hello, world"。在一个逐字字符串数据符号中,分割符间的字符通常认为是逐字的,只有引用转意序列例外。特别的是,简单转意序列和十六进制转意序列在逐字字符串数据符号中不支持。一个逐字字符串数据符号可能会跨越很多行。

string-literal: regular-string-literal verbatim-string-literal

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 51

C# LANGUAGE REFERENCE

regular-string-literal: " regular-string-literal-charactersopt "

regular-string-literal-characters: regular-string-literal-character regular-string-literal-characters regular-string-literal-character

regular-string-literal-character: single-regular-string-literal-character simple-escape-sequence hexadecimal-escape-sequence unicode-character-escape-sequence

single-regular-string-literal-character: Any character except " (U+0022), / (U+005C), and new-line

verbatim-string-literal: @" verbatim -string-literal-charactersopt "

verbatim-string-literal-characters: verbatim-string-literal-character verbatim-string-literal-characters verbatim-string-literal-character

verbatim-string-literal-character: single-verbatim-string-literal-character quote-escape-sequence

single-verbatim-string-literal-character: any character except "

quote-escape-sequence: ""

例子

string a = "hello, world"; // hello, world string b = @"hello, world"; // hello, world

string c = "hello /t world"; // hello world string d = @"hello /t world"; // hello /t world

string e = "Joe said /"Hello/" to me"; // Joe said "Hello" string f = @"Joe said ""Hello"" to me"; // Joe said "Hello"

string g = "sever//share//file.txt"; // //server/share/file.txt string h = @"//server/share/file.txt"; // //server/share/file.txt

string i = "one/ntwo/nthree"; string j = @"one two three";

介绍了多种字符串数据符号。最后一个字符串数据j是逐字字符串数据,它横跨了很多行。在引号间的字符,包括空白如转行字符,都是逐字复制的。

2.5.3.6 null 数据字符

null-literal: null

52 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

2.5.4 操作符和标点

这里有许多种操作符和标点。操作符用于表达式来描述操作涉及到一个或多个操作数。例如,表达式a+b使用+操作符来把a和b相加。标点用于组织和分割。例如标点;是用来分割在声明列表中出现的声明。

operator-or-punctuator: one of { } [ ] ( ) . , : ; + - * / % & | ^ ! ~ = < > ? ++ -- && || << >> == != <= >= += -= *= /= %= &= |= ^= <<= >>= ->

2.5.5 Unicode 字符转意字符序列

一个Unicode 字符转意字符序列代表了一个Unicode字符。Unicode 字符转意字符序列在标识符,字符串数据符号和字符数据符号中是被允许的。

unicode-character-escape-sequence: /u hex-digit hex-digit hex-digit hex-digit

不能实现多重转换。例如字符串数据“/u005Cu005C”与 “/u005C” rather than “//”是相等的。 (Unicode 数值/u005C 是字符 “/”。)

例子

class Class1 { static void Test(bool /u0066) { char c = '/u0066'; if (/u0066) Console.WriteLine(c.ToString()); } }

介绍了许多/u0066的使用,它是字母“f”的字符转意序列。这个程序等价于

class Class1 { static void Test(bool f) { char c = 'f'; if (f) Console.WriteLine(c.ToString()); } }

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 53

 

Chapter 错误!未定义样式。 错误!未定义样式。

3. 基本概念

3.1 声明

C#程序中的声明定义了程序的重要声明。C#程序用名称空间(§错误!未找到引用源。)来组织,它可以包含类型声明和嵌套声明。类型声明(§9.5)用来定义类(§10)、结构(§11)、接口(§11)、联合(§14)和代表(§15)。在类型定义中被允许的的成员种类要根据类型声明的形式决定。例如,类的声明可以包含实例构造函数(§10.10)、析构函数(§10.11)、静态构造函数(§10.12)、常数(§10.3)、域(§10.4)、方法(§错误!未找到引用源。)、属性(§10.6)、事件(§10.7)、索引(§10.8)、操作符和嵌套类型。

一个声明在声明所属的声明域定义了一个名称。除了重载构造函数、方法、索引和操作符名称,在一个声明域中有两个或更多介绍有相同名称成员的声明时,是错误的。对一个声明域中,包含有相同名称的不同种类成员是永远不可能的。例如,一个声明域中不能包括有相同名称的域和方法。

这里有许多种不同类型的声明域,如下所示。

• 在所有程序的源文件中,不包括嵌套名称空间声明的名称空间成员声明都是一个单独的组合声明域,称为全局声明域。

• 在所有程序的源文件中,名称空间成员声明和有相同完整正确的名称空间名称的名称空间声明都是一个单独的组合声明域。

• 每个类,结构或接口声明都会创建一个新的声明域。名称通过类成员声明、结构成员什么或接口成员声明介绍到这个声明域中。除了重载构造函数声明和静态构造函数声明,一个类或结构成员声明不能引入与类或结构名称相同的成员。一个类,结构或接口允许方法和索引的重载。此外,一个类或结构允许构造函数和操作符的重载声明。例如,一个类、结构或接口可能会包含多个用相同名称声明的方法,而在他们各自签名(§3.4)中提供了不同的方法声明。注意基类不影响类的声明域。而基本接口不影响一个接口的声明域。这样,一个派生的类或接口可以用和继承的成员相同的名称声明一个成员。这样的一个成员被称为隐藏了继承的成员。

• 每个枚举声明创建一个新的声明域。名称通过枚举成员声明介绍到声明域中。

• 每个块或者转换块 为局部变量创建一个分立声明域。名称通过局部变量声明被引入到这个声明域。如果一个块是构造函数或方法声明的主体,在形参列表中声明的参数是这个块的局部变量声明域的成员。块的局部变量声明域包括任何嵌套块。因而,在一个嵌套块中不太可能用与嵌套块中的局部变量有相同名称声明一个局部变量。

• 每个块或转换块为标签创建一个分立的声明域。名称通过标签声明被引入到这个声明域,而名称通过goto声明引入到这个声明域。块的局部变量声明域包括任何嵌套块。因而,在一个嵌套块中不太可能用与嵌套块中的标签相同名称声明一个标签。

在名称被声明的文本顺序通常并不重要。特别是,文本顺序对于声明和使用名称空间、类型、常数、方法、属性、事件、索引、操作符、构造函数、析构函数和静态构造函数来说并不重要。声明顺序在下面的途径中才是重要的:

• 域声明和局部变量的声明顺序决定了他们的初始化是按什么顺序执行。

• 局部变量必须在他们被使用前定义(§3.5)。 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 55

C# LANGUAGE REFERENCE

• 当常数表达式数值被忽略,枚举成员声明的声明顺序是重要的(§14.2) 。

名称空间的声明域是“开放的”,而两个有相同的完全名称的名称空间声明将放到相同的名称空间空间中。例如

namespace Megacorp.Data { class Customer { ... } }

namespace Megacorp.Data { class Order { ... } }

上面声明的两个名称空间声明捐献到相同的声明域,这里声明了两个有完全合格名称的类Megacorp.Data.Customer和Megacorp.Data.Order。因为两个声明属于相同的声明域,如果每个都包含一个对相同名称的类的声明,就会产生错误。

块的声明域包括任何嵌套块。这样,在下面的例子中,方法F和G有错误,因为名称I已经在outer块中声明了,就不能再在inner块中声明。然而,由于两个i是在分立的非嵌套块中声明的,方法H和I是有效的。

class A { void F() { int i = 0; if (true) { int i = 1; } }

void G() { if (true) { int i = 0; } int i = 1; }

void H() { if (true) { int i = 0; } if (true) { int i = 1; } }

void I() { for (int i = 0; i < 10; i++) H(); for (int i = 0; i < 10; i++) H(); } }

56 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

3.2 成员

名称空间和类型都有成员。一个实体的成员通常通过使用一个有效的名称来得到,此名称从一个对实体的引用开始,跟着一个“.” 代号,然后是成员的名称。

类型的成员或者在类型中声明或者从类型的基类继承。当从基类继承一个类型,基类中除了构造函数和析构函数的所有成员都称为派生类型的成员。一个基类成员声明可访问性并不控制是否成员被继承,除了构造函数和析构函数的其它任何成员都可以被继承。然而,一个被继承的成员也许不能在派生的类型中进行访问,或者因为它的声明可访问性(§错误!未找到引用源。)或者是因为他被类型中自己的声明隐藏了(§3.5.1.2)。

3.2.1 名称空间成员

没有嵌套名称空间的名称空间和类型是全局名称空间的成员。这直接与在全局声明域里声明的名称相符合。

在某个名称空间中定义的名称空间和类型是那个名称空间的成员。这直接与在声明域里声明的名称相符合。

名称空间没有访问限制。不能声明私有、保护或内部的名称空间,并且名称空间名称通常是公共可访问的。

3.2.2 结构成员

一个结构的成员是在这个结构中声明的而且从类object中继承的成员。

与结构类型成员直接相关的简单类型的成员由简单类型给出别名:

• sbyte 的成员是结构System.SByte 的成员。

• byte的成员是结构System.Byte的成员。

• short的成员是结构System.Int16的成员。

• ushort的成员是结构System.UInt16的成员。

• int的成员是结构System.Int32的成员。

• uint的成员是结构System.UInt32的成员。

• long的成员是结构System.Int64的成员。

• ulong的成员是结构System.UInt64的成员。

• char的成员是结构System.Char的成员。

• float的成员是结构System.Single的成员。

• double的成员是结构System.Double的成员。

• decimal的成员是结构System.Decimal的成员。

• bool的成员是结构System.Boolean的成员。

3.2.3 枚举成员

一个枚举的成员是在枚举中声明的常数,而且这些成员从类object中继承。 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 57

C# LANGUAGE REFERENCE

3.2.4 类成员

一个类的成员是在此类中声明的成员,并且是从基类中继承的成员(除了类object没有基类)。从基类中继承的的成员包括基类中的常数、域、方法、属性、事件、索引、操作符和类型,但是不包括基类的构造函数、析构函数和静态构造函数。基类的成员不管他们的访问能力就可以继承。

一个类声明可能包括对常数、域、方法、属性、事件、索引、操作符、构造函数、析构函数、静态构造函数和类型的声明。

Object和string的成员直接与由他们定别名的类类型成员相对应:

• object的成员是类System.Object的成员。

• string的成员是类System.String的成员。

3.2.5 接口成员

一个接口的成员是在此接口中和所有这个接口的基础接口中声明的成员,并且这个接口从类object中继承。

3.2.6 数组成员

数组的成员是从类System.Array继承的成员。

3.2.7 代表成员

代表的成员是从类System.Delegate继承的成员。

3.3 成员访问

成员的声明允许通过成员访问来控制。成员的可访问性是由 一些成员的已经声明的访问性建立的,如果有立即包含(containing)类型,这些成员就同这些立即包含类型相结合。

当访问一个特殊成员被允许时,成员被称为可访问的。相反,当对一个成员的访问被禁止,这个成员就被称为不可访问的。当成员的可访问域(§3.3.2)中包括了访问发生的文本地址时,就允许对一个成员进行访问。

3.3.1 声明可访问性

一个成员的声明可访问性可以是下面几个之一:

• 公共的,它通过在成员声明中加public修饰符来选择。公共的直觉意义是“无限制访问”。

• 保护的内部的(意思是保护或内部)是通过在成员声明中包括一个protected和一个internal修饰符来选择。保护的内部的直觉意义是“对这个程序或从包含类中继承的类型的访问受限制”。

• 保护的,它通过在成员声明中包括protected修饰符来选择。保护的直觉意义是“对从包含类或从包含类中继承的类型的访问受限制。”

• 内部的,它通过在成员声明中包括internal修饰符来选择。内部的直觉意义是“对这个程序的访问受限制”。

• 私有的,它通过在成员声明中包括private修饰符来选择。私有的直觉意义是“对于包含类型的访问受限制”。 58 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

根据成员声明发生地方的上下文,只有特定的声明可访问性被允许。而且,当一个成员声明不包括任何存储修饰符时,声明发生地方的上下文决定默认的声明可访问性。

• 名称空间隐含有一个public声明可访问性。在名称空间声明中不需要访问修饰符。

• 在编译单元或名称空间中的类型声明可以用public或internal声明可访问性,而默认的是内部声明可访问性。

• 类成员可以是五种声明可访问性中的任意一个,默认的是私有声明可访问性。(注意一个声明为一个类的成员的类型可以有五个声明可访问性的任何一个,然而一个声明为名称空间成员的类型只能是公共或内部声明可访问性。

• 结构成员可以是公共、内部或私有声明可访问性,默认的是私有声明可访问性。结构成员不能有保护或者内部保护声明可访问性。

• 接口成员隐含有公共声明可访问性。在接口成员声明中不允许访问修饰符。

• 枚举成员隐含有公共声明可访问性。在枚举成员声明中不允许访问修饰符。

3.3.2 可访问性域

一个成员的可访问性域是(也许是脱节的 )程序文字的一部分,在这里,允许对成员进行访问。为了定义一个成员的可访问性域,如果不在类型里声明,一个成员就被说成是顶级的,而如果它在另外一个类型里声明,这个成员就被称为嵌套的。而且,程序的程序文字就像所有包括在程序源文件中的程序文字一样定义,而一个类型的程序文字就像所有在类、结构接口或枚举的结构体中包含在开始和结束符号“{” 和 “}”中的程序文字一样定义(可能包括有嵌套的类型)。

一个预定义类型(例如object、int或double)的可访问性域是没有限制的。

一个在程序P中声明的顶级类型T的可访问性域定义如下:

• 如果T的声明可访问性是公共的,那么T的可访问性域是P的程序文字和任何引用P的程序。

• 如果T的声明可访问性是内部的,T的声明可访问性就是P的程序文字。

从这些它所跟随的定义来看,顶级类型的可访问性域通常至少是声明类型的程序的程序文字。

程序P中在类型T中声明的成员M的可访问性域的定义如下(注意M本身也许就是个类型):

• 如果M的声明可访问性是公共的,M的可访问性域是T的可访问性域。

• 如果M的声明可访问性是内部保护的,M的可访问性域就是T的可访问性域和P的程序文字的交集和在P外面声明的并且从T继承的程序文字。

• 如果M的声明可访问域是保护的,M的可访问性域就是T的可访问性域和T的程序文字的交集和任何从T中继承的类型。

• 如果M的声明是内部的,M的可访问性域就是T的可访问性域和P的程序文字的交集。

• 如果M的声明是私有的,M的可访问性域就是T的程序文字。

从这些它所跟随的定义来看,嵌套成员的可访问性域通常至少是声明成员的类型的程序文字。而且,一个成员的可访问性域永远不会比成员被声明的类型的可访问性域包含更多。

从直觉来说,当一个类型或成员M被访问,下面的步骤就是进行估计以确保访问被允许: Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 59

C# LANGUAGE REFERENCE

• 首先,如果M按一个类型声明(与一个编译单元或名称空间相对),如果那个类型是不可访问的,就会发生错误。

• 这样,如果M是公共的,访问就被允许。

• 另外,如果M是内部保护的,如果访问发生在M被声明的程序中访问就是被允许的,或者如果访问发生在从M被声明的类继承的类中并且是通过派生类类型发生(§错误!未找到引用源。),访问也是被允许的。

• 另外,如果M是保护的,如果访问发生在M被声明的类中,或者在从M被声明的类继承的类中并且是通过派生类类型发生(§错误!未找到引用源。),访问就是被允许的。

• 另外,如果M是内部的,如果发生在M被声明的程序中,访问就是被允许的。

• 另外,如果M是私有的,如果访问发生在M被声明的类型中,访问就是被允许的。

• 另外,如果成员的类型是不可访问的,就会发生错误。

在例子中

public class A { public static int X; internal static int Y; private static int Z; }

internal class B { public static int X; internal static int Y; private static int Z;

public class C { public static int X; internal static int Y; private static int Z; }

private class D { public static int X; internal static int Y; private static int Z; } }

类和成员有下面的可访问性域:

• A和A.X的可访问性域是没有限制的。

• A.Y、 B、 B.X、 B.Y、 B.C、 B.C.X和B.C.Y的可访问性域是包含程序的程序文字。

• A.Z的可访问性域是A的程序文字。

• B.Z和 B.D 的可访问性域是B的程序文字,包括B.C 和 B.D的程序文字。

• B.C.Z的可访问性域是B.C的程序文字。

• B.D.X、 B.D.Y 和 B.D.Z的可访问性域是B.D的程序文字。 60 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

如所示的例子,一个成员的可访问性域永远不会比比包含类型的大。例如甚至所有X成员有公共的声明可访问性,除了A.X外都有被包含类型约束的可访问性域。

如§3.2中所描述,基类中除了构造函数和析构函数的所有成员,都是从派生类型继承的。这甚至包括基类的私有成员。然而,一个私有成员的可访问性域只包括声明成员的类型的程序文字。在例子中

class A { int x;

static void F(B b) { b.x = 1; // Ok } }

class B: A { static void F(B b) { b.x = 1; // Error, x not accessible } }

类B从类A中继承私有成员X。因为成员是私有的 ,所以只有在A的类结构体中才能对它进行访问。这样在方法A.F中允许对b.x的访问,但是在方法B.F中是失败的。

3.3.3 保护的访问

当一个保护成员在他被声明的类的程序文字外被访问,并且当一个内部保护成员在他被声明的程序的程序文字外被访问,访问就要求通过访问发生的派生类中进行。让声明了一个保护成员M的B作为一个基类,并且让D作为从B派生的类。在D的类结构体内,可以通过下面的某种形式来访问M:

• 格式M的一个无效的类型名称和一个基本的表达式。

• 一个格式T.M的基本表达式,T是由D或者从D派生的类提供的。

• 一个格式E.M的基本表达式,E是由D或者从D派生的类提供的。

• 一个格式base.M的基本表达式。

除了这些形式的访问,一个派生类可以在构造函数初始化时访问基类的保护的构造函数 (§10.10.1)。

在这个例子中

public class A { protected int x;

static void F(A a, B b) { a.x = 1; // Ok b.x = 1; // Ok } }

public class B: A { static void F(A a, B b) { a.x = 1; // Error, must access through instance of B b.x = 1; // Ok } }

由于访问或者通过A的实例发生或者在A的派生类中发生,因此,在A中可以通过A和B的实例来访问X。然而,由于A不是从B中派生的,因此在B中不可能通过A的实例访问x。 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 61

C# LANGUAGE REFERENCE

3.3.4 可访问性约束

C#语言中的许多结构需要一种至少和一个成员或其它类型相同可访问的类型。如果T是M可访问性域的一个超集,那么类型T就要求至少可和成员或类型M一样可访问。换句话说,如果T在所有M可访问的上下文中都可访问,那么T至少和M一样可访问。

下面的有一些可访问性的约束:

• 一个类类型的直接基类必须至少同类类型本身同样可访问。

• 一个接口类型的外部基本接口必须至少同接口类型本身同样可访问。

• 代表类型的返回类型和参数类型必须至少同代表类型本身同样可访问。

• 常数的类型必须至少同常数本身同样可访问。

• 域的类型必须至少同域本身同样可访问。

• 一个方法的返回类型和参数类型必须至少同方法本身同样可访问。

• 属性的类型必须至少同属性本身同样可访问。

• 事件的类型必须至少同事件本身同样可访问。

• 参数的类型必须至少同索引本身同样可访问。

• 一个操作符的返回类型和参数类型必须至少同操作符本身同样可访问。

• 构造函数的参数类型必须至少同构造函数本身同样可访问。

在这个例子中

class A {...}

public class B: A {...}

因为A不能与B一样可访问,所以类比是有错误的。

同样,在例子中

class A {...}

public class B { A F() {...}

internal A G() {...}

public A H() {...} }

因为A不能与B一样可访问,所以B中的方法H也是有错误的。

3.4 签名和重载

方法、构造函数、标签和操作符按照它们的签名分类:

• 一个的签名由方法和对象的名称、修饰符和它的形参的类型组成。方法的签名中不包括返回类型。

• 一个函数的签名由数字修饰符和它的形参的类型组成。

• 一个标签的签名是由数字和它的形参的类型组成。标签的签名不包括元素类型。

• 操作符的签名由操作符的名称和数字还有它的形参的类型组成。操作符的签名不包括结果类型。 62 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

签名激活了类、结构和接口中成员的重载机制:

• 方法的重载允许类、结构或接口用相同的名称声明多个方法,并且所提供的方法的签名都是唯一的。

• 构造函数的重载允许一个类或结构声明多个构造函数,所提供的构造函数的签名都是唯一的。

• 标签的重载允许类、结构或接口声明多个标签,所提供的标签的签名都是唯一的。

• 操作符的重载允许一个类或结构用相同的名称声明多个操作符,所提供的操作符的签名都是唯一的。

下面的例子介绍了一系列方法声明重载和它们的签名。

interface ITest { void F(); // F()

void F(int x); // F(int)

void F(ref int x); // F(ref int)

void F(out int x); // F(out int)

void F(int x, int y); // F(int, int)

int F(string s); // F(string)

int F(int x); // F(int) }

注意参数修饰符是签名的一部分。这样F(int)、 F(ref int) 和 F(out int)都是唯一的签名。此外注意第二个和最后一个方法的声明的返回类型,它们的签名都是F(int)。这样,在编译上面的例子时会在第二个和最后一个例子产生错误。

3.5 范围

一个名字的范围就是程序文本的区域,在这个范围内可以通过声明的名称来查找实体,名称没用条件限制。范围可以嵌套,并且内层的范围可以对从外层范围来的名称的意义进行再次声明。从外层范围来的名称称为在内层范围覆盖的程序文本区域内被隐藏了,并且对外层名称的访问只能通过限定名称。

• 在被不包括嵌套名称空间声明的名称空间成员声明中表示的名称空间成员的范围对于每个编译单元的程序文本来说是完整的。

• 在一个全称为N的名称空间空间声明中由名称空间成员声明表示的名称空间成员的范围是任何一个全称为N或由与N有相同的修饰符序列的名称空间声明的名称空间结构体。

• 通过使用指示符定义或引入名称的范围扩展到使用指示符发生的编译单元或名称空间结构体的名称空间成员声明中。一个使用指示符可能会使得在某个编译单元或名称空间结构体中得到从0到多个名称空间或类型名称,但是并不把任何新成员放到主要声明域中。换句话说,使用指示符并没有传递,而只是影响它出现的编译单元或名称空间结构体。

• 在类成员声明中表示的成员的范围是表示所在的类结构体。另外,一个类成员的范围扩展到派生类的类结构体,这些派生类包含在成员的可访问性域 (§3.3.2)中。

• 在结构成员声明中表示的成员的范围表示所在的结构体。

• 在枚举成员声明中表示的成员的范围是声明所在的枚举结构体。

• 在构造函数声明中表示的参数的范围,是这个构造函数声明的构造函数初始化程序和块。 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 63

C# LANGUAGE REFERENCE

• 在方法声明中表示的参数的范围是这个方法声明的方法结构体。

• 在标签声明中表示的参数的范围是这个标签声明的访问声明。

• 在操作符声明中表示的参数的范围是这个操作符声明的块。

• 在局部变量声明中表示的局部变量的范围是声明出现的块。 从执行局部变量的变量声明的文本位置引用局部变量是错误的。

• 在for声明的for初始化程序中表示的局部变量的范围是for声明的for初始化、for条件、for(循环指示符)和所包含的声明。

• 在标签声明中表示的标签的范围是表示所在的块。

在名称空间、类、结构或枚举成员的范围中,可以把成员引用到执行对对象的声明的文字位置。例如

class A { void F() { i = 1; }

int i = 0; }

这里,对于F来说在没有被表示前把I引入是有效的。

在一个局部变量的范围中,在执行局部变量变量说明符的文本位置引入局部变量是错误的。例如

class A { int i = 0;

void F() { i = 1; // Error, use precedes declaration int i; i = 2; }

void G() { int j = (j = 1); // Legal }

void H() { int a = 1, b = ++a; // Legal } }

在上面的方法F中,第一次对I赋值很明确没有引用在外层范围声明的域。它指向局部变量而且它是错误的,因为它从文字上在变量的声明之前。在方法G里,在初始化程序中使用j来声明是合法的,因为使用并没有在变量声明之前。在方法H中,一个后来的变量声明合法的引用了一个局部变量,这个局部变量在早先的变量声明时声明过了。

对于局部变量的确定范围的规则是要保证用于表达式中的名称的意义与块中的相同。如果局部变量的范围只是从他的声明延伸到块结束的地方,那么就像上面的例子中所示,第一个赋值将赋给实例变量,而第二个赋值将赋给局部变量,如果块的声明在以后被重新分配,就有可能导致错误。

在块内的名称的意义可能会根据使用名称的上下文有所不同。在例子中 64 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

class Test { static void Main() { string A = "hello, world"; string s = A; // expression context

Type t = typeof(A); // type context

Console.WriteLine(s); // writes "hello, world" Console.WriteLine(t.ToString()); // writes "Type: A" } }

名称A用在一个表达式中来引用局部变量A,而在一个类型中引用类A。

3.5.1 名称隐藏

典型情况下,一个实体的范围比实体本身的声明域要涉及更多的程序文字。特别是,一个实体的范围可能会包括声明,这些声明会引入一些包含有相同名称的实体的新声明域。这样的声明会使原来的实体变为隐藏。相反,当一个实体不是隐藏的时候,称为可视。

当嵌套引起范围重叠时和当继承引起范围重叠时,就会出现名称隐藏。两种隐藏的特征在下面的章节中描述。

3.5.1.1 通过嵌套隐藏

出现在当名称空间中的名称空间和类型的嵌套,或者类和结构中的类型嵌套,或者是参数和局部变量声明的都会造成通过嵌套隐藏名称的出现。通过嵌套隐藏名称通常是“安静地”发生,也就是当外部名称被内部名称隐藏起来时,不会报告错误和警告。

在这个例子中

class A { int i = 0;

void F() { int i = 1; }

void G() { i = 1; } }

在方法F中,实例变量I被局部变量I隐藏了,但是在方法G中,I还是引用实例变量。

当一个内部范围的名称隐藏一个外部范围的名称,它会把那个名称的所有重载都隐藏。在例子中

class Outer { static void F(int i) {}

static void F(string s) {}

class Inner { void G() { F(1); // Invokes Outer.Inner.F F("Hello"); // Error } Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 65

C# LANGUAGE REFERENCE

static void F(long l) {} } }

对F(1)的调用实际调用了在内层声明的F,因为所有F的外部事件都被内部声明隐藏了。出于相同的原因,调用F("Hello")是错误的。

3.5.1.2 通过继承隐藏

名称通过继承隐藏在类或结构重新对从基类继承来的名称声明时发生。这种类型的名称隐藏有下面形式中的一种:

• 一个引入类或结构的常数、域、属性、事件或类型隐藏了所有基类中名称相同的成员。

• 一个引入类或结构的方法隐藏了所有有相同名称的非方法基类成员和所有有相同签名的基类成员(方法名称和参数数目、修饰符和类型)。

• 一个引入类或结构的索引隐藏所有有相同签名的基类索引(参数数目和类型)。

管理操作符声明(§10.9)的规则使得一个派生类可以用与基类中的一个操作符相同的签名声明一个操作符。这样,操作符就不会隐藏其它的操作符。

与隐藏外部范围的名称相反,隐藏继承范围的可访问名称就会报告警告。在例子中

class Base { public void F() {} }

class Derived: Base { public void F() {} // Warning, hiding an inherited name }

在Derived中F的声明产生了一个警告报告 。隐藏一个继承的名称肯定不是错误,因为这样将排除基类单独更新。例如,因为Base后来的版本把不在先前版本类中的F方法引入,这样就会产生上面的情况。如果上面的情况是一个错误,那么任何对于在独立版本类库中的基类的变更就会潜在地使得派生类变为无效。

由于继承名称引起的警告可以通过使用新修饰符来消除:

class Base { public void F() {} }

class Derived: Base { new public void F() {} }

新的修饰符指出Derived中的F是“新”,并且它实际上是要隐藏继承成员。

新成员的声明只是在新成员的范围内隐藏继承的成员。

class Base { public static void F() {} }

66 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

class Derived: Base { new private static void F() {} // Hides Base.F in Derived only }

class MoreDerived: Derived { static void G() { F(); } // Invokes Base.F }

在上面的例子中,Derived中F的声明隐藏了从Base继承的F,但是由于Derived中的新F有私有的可访问性,所以它的范围不会扩展到MoreDerived。这样,MoreDerived.G中对F()的调用是有效的,并且会引用Base.F。

3.6 名称空间或类型名称

C#程序中的许多上下文都需要名称空间名称和类型名称来确认。名称的任何形式都是写作一个或多个由“.”符号分开的标识符。

namespace-name: namespace-or-type-name

type-name: namespace-or-type-name

namespace-or-type-name: identifier namespace-or-type-name . identifier

一个类型名称是一个指向某个类型的名称空间或类型名称。后面的分析如下描述,类型名称的名称空间或类型名称必须指向一个类型,如果是其它就会产生错误。

一个名称空间名称是一个指向名称空间的名称空间名称或类型。后面的分析如下描述,名称空间名称的名称空间或类型名称必须指向一个类型,如果是其它就会产生错误。

名称空间或类型名称的意义如下所示:

• 如果名称空间或类型名称由单独的指示符组成:

• 如果名称空间或类型名称出现在类或结构的声明之内,那么 从类或结构声明开始并且在嵌套类或结构声明中延续(如果有),如果有所给名称的成员存在,是可访问的并且指示了一种类型,那么名称空间或类型名称指向那个成员。注意无类型成员(构造函数、常数、域、方法、属性、索引和操作符)在确定名称空间或类型名称时是被忽略的。

• 否则,如果有名称空间或类型名称出现(如果有)的声明,同每个嵌套名称空间声明(如果有)一起,并且由全局名称空间结束,下面的步骤会被估计直到实体的位置确定:

• 如果名称空间包含一个给定名称的名称空间成员,那么名称空间或类型名称指向那个成员并且根据成员被分类为一个名称空间或一个类型。

• 否则,如果名称空间声明包括一个把一个引入的名称空间或类型和一个给定名称联系起来的使用别名指示,那么名称空间或类型名称指向那个名称空间或类型。

• 否则,如果由名称空间声明的使用名称空间指示引入的名称空间包含一个确切类型,那么名称空间或类型名称指向那个类型。 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 67

C# LANGUAGE REFERENCE

• 否则,如果由名称空间声明的使用名称空间指示引入的名称空间包含多个确切类型,那么名称空间或类型名称是不明确的,并且会发生错误。

• 否则,名称空间或类型名称就是未定义的并且会发生错误。

• 否则,名称空间或类型名称是N.I的形式,这里N是一个有所有除了最右边一个指示符组成的名称空间或类型名称,而I是最右边的指示符。N是最先确定的名称空间或类型名称,如果N的确定不成功,那么就会发生错误,否则,N.I如下确定:

• 如果N是名称空间而I是那个名称空间中一个可访问的成员,那么N.I指向那个成员并且根据成员分类为名称空间或类型。

• 如果N是一个类或类型而I是N中的一个可访问类型,那么N.I指向那种类型。

• 否则,N.I是一个无效的名称空间或类型名称,并且会产生一个错误。

3.6.1 合格的名称

每个名称空间和类型都有一个完全限制名称,它在其它名称空间或类型中间唯一确定某个名称空间或类型。一个名称空间或变量的完全限制的名称N由下面决定:

• 如果N是全局名称空间的一个成员,它的完全限制的名称就是N。

• 否则,它的完全限制的名称是S.N,这里S是声明了N的名称空间或类型的完全限制的名称。

换句话说,N的完全限制的名称是指向N的标识符的完全分等级路径,从全局名称空间开始。因为名称空间或类型的每个成员都要有一个唯一的名称,它是由于一个名称空间或类型的完全限制的名称总是唯一的造成。

下面的例子介绍了许多名称空间和类型声明,和与它们相对应的完全限制的名称。

class A {} // A

namespace X // X { class B // X.B { class C {} // X.B.C }

namespace Y // X.Y { class D {} // X.Y.D } }

namespace X.Y // X.Y { class E {} // X.Y.E }

68 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

4. 类型

C#语言的类型被分为三类:数值类型、引用类型和指针类型。

type: value-type reference-type pointer-type

指针类型只能用在不安全代码,并且将在§18.2中进行讨论。

数值类型与引用类型所不同的是,数值类型变量直接含有它们的数据,然而引用类型的变量存储对它们的数据的引用,就是后面要介绍的对象。对于引用类型,可能会出现两个变量引用相同对象的情况,这样对于一个变量的的操作就有可能影响到由其它变量引用的对象。对于数值类型,每个变量都有它们自己对数据的拷贝,这样就不可能出现一个对变量的操作影响到另外一个的情况。

C#的类型系统是统一的,这样任何类型的数据都可以被看做对象。C#中的任何类型都直接或间接地从objiect类类型派生,而object是所有类型的最基本类类。引用类型的数值被看做通过对象,这些对象通过把数值看做类型对象来简化。数值类型的数 值通过包装和解包操作来被当做对象 (§4.3)。

4.1 数值类型

数值类型既是一个结构类型也是枚举类型。C#提供了一系列预定义结构类型,称为简单类型。简单类型通过保留字指定,并且进一步分成数字类型,整数类型和浮点数类型。

value-type: struct-type enum-type

struct-type: type-name simple-type

simple-type: numeric-type bool

numeric-type: integral-type floating-point-type decimal

integral-type: sbyte byte short ushort int uint long ulong char

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 69

C# LANGUAGE REFERENCE

floating-point-type: float double

enum-type: type-name

所有数值类型都隐式地从类object继承。不允许任何类型从数值类型派生,因而数值类型是被默认封闭的。

一个数值类型的变量通常包含一个那种类型的数值。不像引用类型,数值类型的数值不能为null或是引用一个进一步派生类型的变量。

对某个数值类型的变量赋值就会创建一个对所赋数值的拷贝,它复制了引用而不是引用所指定的对象。

4.1.1 默认构造函数

所有类型都隐含地声明了一个公共的无参数的构造函数,称为默认构造函数。默认构造函数返回一个初始值为零的实例,为数值类型的默认数值:

• 对于所有单独类型,默认数值是由一个零位格式产生的数值:

• 对于sbyte、byte、short、ushort、int、uint、long和ulong,默认的数值为0。

• 对于char,默认的数值为'/x0000'。

• 对于float,默认的数值是0.0f。

• 对于double,默认的数值为0.0d。

• 对于decimal,默认的数值为0.0m。

• 对于bool,默认的数值为false。

• 对于一个枚举类型E,默认数值是0。

• 对于结构类型,默认数值是把所有数值类型域设为它们的默认类型并且把所有引用类型域设为null的数值。

像其它任何构造函数一样,一个数值类型的默认的构造函数用new操作符调用。在下面的例子中,变量i和j都初始化为0。

class A { void F() { int i = 0; int j = new int(); } }

因为每个数值类型隐含的都有公共无参数构造函数,所以让一个结构类型包含一个外部声明的无参数构造函数是不可能的。一个结构类型可以允许声明一个参数化的构造函数。例如

struct Point { int x, y;

70 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

public Point(int x, int y) { this.x = x; this.y = y; } }

如果已经给出上面的声明,那么语句

Point p1 = new Point(); Point p2 = new Point(0, 0);

都会创建一个Point,其中x和y被初始化为0。

4.1.2 结构类型

一个结构类型是一个数值类型,它可以声明构造函数、常数、域、方法、属性、索引、操作符和嵌套类型。结构类型在§11中描述。

4.1.3 简单类型

C#提供了一系列的预定义结构类型,称为简单类型。这些简单类型通过关键词确定,但是这些关键词可以为在System名称空间中的预定义结构类型关键词起简单的别名,就像下面表中所示。

关键字

有别名的类型

sbyte

System.SByte

byte

System.Byte

short

System.Int16

ushort

System.Uint16

int

System.Int32

uint

System.Uint32

long

System.Int64

ulong

System.Uint64

char

System.Char

float

System.Single

double

System.Double

bool

System.Boolean

decimal

System.Decimal

一个简单类型和它有别名的结构类型是不可分辨的。换句话说,当写下保留字byte时和写System.Byte确实没有什么区别,并且用System.Int32也与用保留字int相同。

因为一个简单类型代表了一个结构类型,所以每个简单类型都有成员。例如,int有在System.Int32中声明的成员和从System.Object中继承的成员,并且下面的语句是允许的:

int i = int.MaxValue; // System.Int32.MaxValue constant string s = i.ToString(); // System.Int32.ToString() instance method string t = 123.ToString(); // System.Int32.ToString() instance method

注意,整数文字上是int类型数据,并且同时也是System.Int32结构类型的数据。

简单类型与其它结构类型,其它结构类型允许包含附加操作符: Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 71

C# LANGUAGE REFERENCE

• 大多数简单类型允许通过使用文字来 创建 (§错误!未找到引用源。)。例如,123是int类型量,而'a'是字符类型量。C#使得不用对其它结构类型文字进行预定义,而其它结构类型数据基本上是通过那些结构类型的构造函数来创建。

• 当一个表达式的运算符都是简单类型常数时,编译器在编译时就可以对这个表达式进行赋值。这样一个表达式称为常数表达式(§错误!未找到引用源。)。包括其它结构类型定义的操作符的表达式通常意味着运行时赋值。

• 通过const声明,就有可能声明一个简单类型(§10.3)的常数。不可能有其它结构类型的常数,但是static readonly域提供了相似的作用。

• 包括简单类型的转换可以参加由其它结构类型定义的转换操作符的赋值,但是用户定义的转换操作符不能参与另外一个用户定义操作符的赋值(§错误!未找到引用源。)。

4.1.4 整数类型

C#支持九种整数类型: sbyte、byte、short、ushort、int、uint、long、ulong和 char。这些整数类型有下面的大小和数值范围:

• sbyte类型表示有符号的8位整数,数值范围为-128到127。

• byte 类型表示无符号8位整数,数值范围为0到255。

• short 类型表示有符号16位整数,数值范围为-32768到32767。

• ushort类型表示无符号16位整数,数值范围为0到65535。

• int类型表示有符号32位整数,数值范围为–2147483648到2147483647。

• uint类型表示无符号32位整数,数值范围为0到4294967295。

• long类型表示有符号64位整数,数值范围为–9223372036854775808到9223372036854775807。

• ulong类型表示无符号64位整数,数值范围为0到18446744073709551615。

• char类型表示无符号16位整数,数值范围为0到65535。char类型的可能数值集符合Unicode字符集。

整数类型一元和二元操作符总是按有符号32位精度、无符号32位精度、有符号64位精度或无符号64位精度进行操作。

• 对于一元+和~操作符,操作数被转换为类型T,这里T是int、uint、long和ulong中第一个可以完全代表操作数的所有可能值的类型。操作使用类型T的精度来实现,而结果的精度也是T。

• 对于一元操作符-,操作数被转换为类型T,这里T是int和long中第一个可以完全代表操作数的所有可能值的类型。操作使用类型T的精度来实现,而结果的精度也是T。一元操作符-不能应用于ulong类型操作数。

• 对于二元操作符+、–、*、/、%、&、^、|、==、!=、>、<、>=和<= 操作符,操作数被转换为类型T,这里T是int、uint、long和ulong中第一个可以完全代表操作数的所有可能值的类型。操作使用类型T的精度来实现,而结果的精度也是T(或相关操作符bool)。

• 对于二元操作符<<和>> 操作符,操作数被转换为类型T,这里T是int、uint、long和ulong中第一个可以完全代表操作数的所有可能值的类型。操作使用类型T的精度来实现,而结果的精度也是T

char类型被分类为一种整数类型,但是它在两点上不同于其它整数类型: 72 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

• 没有从其它类型到字符类型的隐含的转换。甚至,即使sbyte、byte和ushort类型的数据完全可以用char类型代表,但是从sbyte、 byte和ushort类型到char的隐含转换也不存在。

• char类型的常数必须写成字符文字。字符常量可以只是写成与一个斜杠结合的整数文字。例如, (char)10与'/x000A'相同。

checked和unchecked操作符和语句用来控制检查整数类型算术操作和转换(§7.5.13)的溢出。在一段checked上下文中,一个溢出产生一个编译时错误或者引起扔出一个OverflowException。在一段unchecked的上下文里,溢出被忽略并且不需要送到目标类型的任何高端位被丢弃。

4.1.5 浮点类型

C#支持两个浮点类型: float和double。float和double类型用32位单精度和64位双精度IEEE754格式来表示,它提供了一系列数值:

• 正零和负零。在大多数情况下,正零和负零与简单的零值相同,但是它们的使用中间有一些区别。

• 正无穷大和负无穷大。无穷大是由一个非零成员除以零的操作产生的。例如,1.0 / 0.0产生正无穷大,而–1.0 / 0.0产生负无穷大。

• 非数字数据,通常缩写为NaN。NaN是无效的浮点数操作产生的,例如零除以零。

• 形如s × m × 2e 的非零数据有限集,这里s是1或者-1,而m和e由具体浮点数类型决定:对于float,0 < m < 224 和−149 ≤ e ≤ 104,对于double,0 < m < 253 和−1075 ≤ e ≤ 970。

float类型可以代表的数值范围大约从1.5 × 10−45 到3.4 × 1038,有7位数字位精度。

double类型可以代表的数值范围大约从5.0 × 10−324 到1.7 × 10308 ,有15到16位数字位精度。

如果二元运算符的一个操作数是浮点类型,那么其它操作数必须是整数类型或者是浮点数类型,并且操作按下面求值:

• 如果一个操作数是整数类型,那么那个操作数会被转换为与其它操作数一样的浮点数类型。

• 如果操作数是double类型,其它 操作数就要转换为double,操作就要按照double类型的范围和精度来进行,而且计算的结果也是double类型(对于相关操作,或者是bool)。

• 否则,操作至少使用float的范围和精度,而且计算的结果也是float类型(对于相关操作,或者是bool)。

包括赋值操作符的浮点操作符,从不产生异常。在异常情况下,浮点数操作会产生下面介绍的零、无穷大或NaN作为替代:

• 如果浮点数操作的结果对于目标形式来说太小,操作的结果就会转换为正零或负零。

• 如果浮点数操作的结果对于目标形式来说太大,操作的结果就会转换为正无穷大或负无穷大。

• 如果浮点数的操作是无效的,操作的结果就会转换为NaN。

• 如果一个或所有浮点操作的操作数都是NaN,那么操作的结果就变为NaN。

浮点数操作可以用比操作结果的类型更高的精度来执行。例如,一些硬件结构支持一个比double类型更大范围和更高精度的“扩展的”或“long double”浮点数类型,并且会隐含地使用这个更高的精度来实现浮点数操作。只有在性能要额外付出时,这样的硬件结构才会被用来实现精度小一些的浮点数操作,而不需要执行同时丧失性能和精度,C#允许所有的浮点数操作使用更高的精度类型。与给出更高精度的结果不同,这样几乎没有任何可测量的影响。在形如x * y / z 的表达式中,这里的乘法产生一

Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 73

C# LANGUAGE REFERENCE

个超出double类型范围的结果,但是后面的除法带来一个回到double范围的暂时结果,实际上在大一些的范围形式计算这个表达式会产生有限的结果而不是无穷大。

4.1.6 十进制类型

十进制类型是一个128位数据类型,适合金融和货币计算。十进制类型可以代表的数值范围是从1.0 × 10−28到大约7.9 × 1028,有28到29个有效数字位。

十进制类型数值的有限集合形式为s × m × 10e ,这里s是1或者-1,0 ≤ m < 296而−28 ≤ e ≤ 0。十进制类型不支持有符号零、无穷大和NaN。

一个十进制数由96位整数和十位幂表示。对于一个绝对数值小于1.0m的十进制数,数据就是第28个十进制位,但是没有更多。对于绝对值大于或等于1.0m的十进制数,数据可能是28或29数字位。与float和double类型相比,如0.1的十进制小数成员可以就用十进制表示。在用float和double表示时,这样的成员经常为无穷小数,使得这些表示有更大的舍入误差。

如果一个二元操作符的操作数是十进制类型,其它操作数也必须是整数类型或十进制类型。如果要使用一个整数类型操作数,在操作被执行前它会被转换为十进制数。

十进制类型的数值的操作就是28或29数字位,但是不会多于28十进制位。结果为最接近的可表示的数值,当结果与两个可表示数值都距离都相等时,选择在最小数据位上为奇数的数值。

如果十进制算术操作产生了一个在舍入后对于十进制形式太小的数据,操作的结果就变为零。如果一个十进制算术操作产生了一个对于十进制形式太大的数据,就会抛出一个OverflowException错误。

十进制类型比浮点类型有更高的精度但是有更小的范围。这样,从浮点数类型转换到十进制类型也许会产生溢出的异常,并且从十进制类型转换到浮点数类型也许会有精度损失。出于这些原因,不存在浮点数类型和十进制类型间的隐式转换,并且也没有显式的情况,在同一个表达式中把浮点数和十进制操作数混合在一起是不可能的。

4.1.7 布尔类型

bool类型表示布尔逻辑量,bool类型的可能值为true和false。

在bool和其它类型间不存在标准的转换。特别是,bool类型与整数类型截然不同,bool数据不能用于使用整数类型的地方,反之亦然。

在C和C++语言中,零整数值或空指针可以被转换为布尔数值false,而非零整数数值或非空指针可以转换为布尔数值true。在C#中,这样的转换由显式地把整数数值和零比较或显式地把对象和null比较来实现。

4.1.8 枚举类型

枚举类型是一种有名称常数的独特类型。每个枚举类型都有前级类型,可以是byte、short、int或long。枚举类型通过枚举声明来定义(§14.1)。

4.2 引用类型

引用类型是一个类类型、一个接口类型、一个数组类型或是一个代表类型。 74 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

reference-type: class-type interface-type array-type delegate-type

class-type: type-name object string

interface-type: type-name

array-type: non-array-type rank-specifiers

non-array-type: type

rank-specifiers: rank-specifier rank-specifiers rank-specifier

rank-specifier: [ dim-separatorsopt ]

dim-separators: ,dim-separators ,

delegate-type: type-name

一个引用数值是对于一个那种类型实例的引用,后面称为对象。特殊数值null是所有引用类型都适用的,并且表示缺乏实例。

4.2.1 类类型

类类型定义了一个包括数据成员(常数、域和事件)、函数成员(方法、属性、索引、操作符、构造函数和析构函数)和嵌套类型。类类型支持继承,因为这种机制派生的类可以对基类进行扩展和特殊化。使用对象创建表达式(§7.5.10.1)来创建类类型的实例。

类类型将在§10讨论。

4.2.2 对象类型

object(对象)类型是所有其它类型的最基本类。C#中的任何一个类型都是直接或间接地从object类类型派生的。

object关键字是预定义System.Object类的简化的别名。使用object跟使用System.Object是相同的,反之亦然。

4.2.3 字符串类型

字符串类型是直接从object派生的包装好的类类型。字符串类的实例表示统一的字符编码标准字符串。

字符串类型的数据写成一串文字 (§错误!未找到引用源。)。 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 75

C# LANGUAGE REFERENCE

string关键字是预定义System.String类的简化的别名。使用string跟使用System.String是相同的,反之亦然。

4.2.4 接口类型

一个接口定义了一个协定。一个实现了接口的类或结构必须遵守它的协定。一个接口也许会从多个基本接口继承,而一个类或结构可以实现多个接口。

接口类型在§13中祥述。

4.2.5 数组类型

数组是一种数据结构,它包含了通过计算索引访问的变量成员。包含于数组中的变量,也称为数组的元素,都有相同的类型,而这个类型被称为数组的类型。

数组类型在§错误!未找到引用源。中祥述。

4.2.6 代表类型

代表是一种指向一个静态方法或一个对象的对象实例和对象方法的数据结构。

在C或C++中与代表相同的是函数指针,但是功能指针只能指向静态函数,而代表可以指向静态和实例方法。在后面,代表不仅存储对于方法的入口点的引用,同时也存储对调用方法的对象实例的引用。

代表类型在§15中祥述。

4.3 包装和解包

包装(boxing)和解包(unboxing)是C#类型系统中重要的概念。它通过允许任何数值类型的数据被转换为任何形式类型的对象提供了数值类型和引用类型间的紧密联系。包装和解包使得对在其中任何类型都可以最终被看作对象的类型系统的统一的观察变为可能。

4.3.1 包装转换

包装转换允许任何数值类型可以隐式地转换为object类型或任何由数值类型实现的接口类型。包装一个数值类型的数据包括对对象实例的定位和把数值类型数据拷贝到那个实例中。

包装数值类型的数据的实际过程,可以通过想像一个对那种类型的包装类的实际例子来解释。对于数值类型T,包装类要按下面定义:

class T_Box { T value;

T_Box(T t) { value = t; } }

对于类型T的数值v的包装现在由执行表达式T_Box(v)来代替,并且返回类型为object的结果实例。这样,语句

int i = 123; object box = i;

从概念上符合 76 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved.

Chapter 错误!未定义样式。 错误!未定义样式。

int i = 123; object box = new int_Box(i);

如上面的T_Box和int_Box的包装类型实际不存在,而被包装数据的动态类型实际上并不是一个类类型。作为替代,类型T的一个被包装的数据有动态类型T,而使用is操作符的动态类型检查可以很方便地引用T。例如,

int i = 123; object box = i; if (box is int) { Console.Write("Box contains an int"); }

将在控制台输出字符串 “Box contains an int”。

包装转换隐式地把被包装的数据进行了备份。这与从引用类型到object类型的转换不同,在那里数据一直引用相同的实例,并被认为几乎不从类型object派生。例如,给出声明

struct Point { public int x, y;

public Point(int x, int y) { this.x = x; this.y = y; } }

下面的语句

Point p = new Point(10, 10); object box = p; p.x = 20; Console.Write(((Point)box).x);

因为在发生把p赋值给box的隐含包装操作时,p被拷贝,所以将在控制台上输出数值10。如果Point被声明为一个类,因为p和box将引用相同的实例,就会输出20。

4.3.2 解包转换

解包转换允许任何object类型或从任何由数值类型实现的接口类型,可以显式地转换为任何数值类型。一个解包操作由几部分动作组成,首先检查object实例是一个所给数值类型的被包装数据,然后把数值从实例中拷贝出来。

参考前面章节描述的假象的包装类型,从对象box到数值类型T的解包转换包括执行表达式((T_Box)box).value。这样,语句

object box = 123; int i = (int)box;

从概念上符合

object box = new int_Box(123); int i = ((int_Box)box).value;

对于为了在运行时提供数值类型的解包转换,源变量数据必须是一个指向一个早先对那个数值类型数据打包创建的对象。如果源变量是null或引用一个不相关的对象,就会抛出一个InvalidCastException错误。 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 77

 

Chapter 错误!未定义样式。 错误!未定义样式。

5. 变量

变量代表数据的实际存储位置。每各变量所能存储的数值由它本身的类型决定。C++语言是一种类型安全语言(type-safe language,TSL),而且C++编译器保证每一个数值被保存在相应的变量中。变量的数值可以通过赋值或者++或--运算符改变。

在变量被赋值以前,变量自身的类型必须被明确地声明。

在下面的章节中我们会提到,变量或者被初始化的或者未初始化的。一个初始化的变量在被定义时被赋予了一个确定的初始值,而未初始化的变量在定义时并未被赋予确定的初始值。对于一个在程序某处被认为具有确定数值的IUA,必然在指向这一位置的所有可能的执行路径上存在赋值操作。

5.1 变量类型

C++共有七种变量类型:静态变量,实例变量,数组元素,数值参数,引用参数,输出参数和局部变量。下面的部分将分别对每一种变量类型做相关描述。

例子:

class A

{

public static int x;

int y;

void F(int[] v, int a, ref int b, out int c) {

int i=1;

c=a+b++;

}

}

x是一个静态变量,y是一个实例变量,v[0]是一个数组元素,a是数值参数,b是引用参数,c是一个输出参数,i是一个局部变量。

5.1.1 静态变量

使用static修饰符定义的变量称为静态变量。静态变量在被创建并加载后生效,当被卸载后失效。

静态变量的初始值为此类型变量的默认值(参见§错误!未找到引用源。节)。

为了方便明确赋值检查,静态变量被认为是初始化过的。

5.1.2 实例变量

一个没有static修饰符声明的域被称为实例变量。 Confidential Material – Copyright  Microsoft Corporation 1999-2000. All Rights Reserved. 79

C# LANGUAGE REFERENCE

5.1.2.1 类中的实例变量

当创建某类的一个实例的时候,隶属于该类的实例变量也被生成,当不再有关于这个实例的引用而且实例的析构函数执行了以后,此实例变量失效。类中实例变量的初始值为这种类型变量的默认值(§5.2)。为了方便进行明确赋值检查,类中的实例变量是初始化过的。

5.1.2.2 结构体中的实例变量

一个结构体中的实例变量与隶属与该结构体的结构体变量寿命相同。换句话说,结构体中的实例变量和其中的其他变量一样被同时创建,并且同时失效。而且该结构体中的实例变量的初始赋值状态和其中的其余变量一致。当一个结构体变量被是初始化过的,结构体的实例变量也是如此;反之如果一个结构体变量是未经初始化的时,结构体的实例变量也是未经初始化的。

5.1.3 数组元素

当任意一个数组实例被创建时,这个数组的元素也被同时创建,当不再有任何正对这个数组实例的引用时,它的元素也就此失效。

数组中每个元素的初始值为该数字元素类型的默认值。为了方便明确赋值检查,所有的数字元素都被认为是初始化的。

5.1.4 数值参数

当一个不带有ref或out修饰参数被声明时,我们称它为数值参数。

当被隶属的函数子句function member(method, constructor, accessor, operator)调用时,数值参数自动生成,同时被赋以调用中的参数值。当函数成员返回后,数值参数失效。

为了方便明确赋值检查,所有的数值参数都被认为是初始化过的。

5.1.5 引用参数

当一个带有ref修饰语的参数被声明时,我们称之为引用参数。

引用参数本身并不创建新的存储空间。同时,引用参数指向函数子句调用中作为参数给出的相关变量表征的存储空间。这样,此形式参数的数值总是等于它所指向的变量。

下面时关于引用参数的赋值规则。请注意它们同§5.1.6节中所给出的输出参数相关规则的区别。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值