本章介绍C#语言所特有的一些代码特性,包括用于源代码分散管理的分布类型(Partial Type)、用于代码编译管理的预处理器指令,以及支持软件文档生成的XML代码注释。
1 分布类型
有时候在一个类的定义中可能包含大量的字段和方法成员,甚至大量的嵌套类型,导致代码行数太多,管理起来很不方便。为此,C#中提供了分布类型概念,允许将一个类的定义分散到多个代码片段之中,而这些代码片段又可以放在不同的源文件中。采用这种方式定义的类叫做分布类。分布类型同样适用于结构和接口,所定义的类型分别叫做分布结构和分布接口。
1.1 分布类型的定义
分布类型通过修饰符partial进行定义,该修饰符可以出现在关键字class、struct和interface之前,用于定义不同的分布类型,例如:
public partial interface IOutput{} public partial class Contact{} public partial struct Table{}
由于类的名称相同,下面两段代码共同组成了一个分布类Contact的定义:
public partial class Contact { private string m_name; } public partial class Contact { private string m_gender; }
一旦某个类型被定义为分布类型,那么在其每一部分的定义中都必须使用partial修饰符。这些部分可以在不同的源文件中,但有以下要求:
• 各部分使用的访问限制修饰符应当相同;
• 各部分不能包含重复的成员的定义,除非该成员属于嵌套的分布类型;
• 各部分不能定义不同的基类
• 它们所在的命名空间的名称必须相同,否则就是分属于不同的程序集的不同类型;
• 它们所分布的各个源文件必须在一起编译。
和访问限制修饰符不同,只要在分布类型的任何部分的定义中使用了abstract或sealed修饰符(注意这两个修饰符不能同时出现),那么该类型就是抽象类型或密封类型。
不允许在分布类型的不同部分定义不同的基类。但对于接口则没有这个限制,所有部有继承的接口(可以重复出现)共同组成了整个分布类型继承的接口。
可以将嵌套类型也定义为分布的,这时有两种情况:
一是嵌套类型所在的外部类型不是分布类型,那么其各部分定义就必须全部在外部类型的当前定义中,例如:
public class Contact { public partial class ChangeEventArgs : EventArgs { ... } public partial class ChangeEventArgs { ... } }
二是嵌套类型所在的外部类型也是分布类型,那么其各部分定义也可以分布在外部类型的不同部分当中,其它要求和一般的分布类型相同,例如:
public partial class Contact { ... public partial class ChangeEventArgs : EventArgs { ... } } public partial class Contact { ... public partial class ChangeEventArgs { ... } }
1.2 分布泛型
泛型类型采用分布类型的定义方式,必须满足对一般分布类型的定义要求。由于泛型的特殊性,分布的泛型在类型参数的使用上还有下列附加规则:
• 泛型的类型参数在每个部分都需要进行声明,而且数量和名称必须完全相同;
• 如果在部分定义中出现了类型限制,这就是对整个泛型的类型限制;
• 如果多个部分定义中都现出了类型限制,那么这些限制必须相同(包括继承限制和构造函数限制),但对书写的次序没有要求;
2 代码中的预处理器指令
2.1 条件编译
先看下面的程序:
#define Detail using System; namespace P18_5 { class PreprocessSample { static void Main() { Console.WriteLine("请输入1~50之间的一个整数:"); int n; while (!int.TryParse(Console.ReadLine(), out n) || n < 1 || n > 50) { Console.WriteLine("请输入1~50之间的一个整数:"); } if (n == 1) { Console.WriteLine("FB(1) = 1"); return; } long l1 = 1, l2 = 2, lTmp; #if(Detail) { for (int i = 1; i < n - 1; i++) { lTmp = 12; l2 = l2 + l1; Console.WriteLine("FB({0}) = {1} + {2} = {3}", i + 2, l1, lTmp, l2); l1 = lTmp; } } #else { for (int i = 1; i < n - 1; i++) { lTmp = 12; l2 = l2 + l1; l1 = lTmp; } } #endif Console.WriteLine("运算结果:FB({0})= {1}", n, l2); } } }
运行结果为:
请输入1~50之间的一个整数: 30 FB(3) = 1 + 12 = 3 FB(4) = 12 + 12 = 15 FB(5) = 12 + 12 = 27 FB(6) = 12 + 12 = 39 FB(7) = 12 + 12 = 51 FB(8) = 12 + 12 = 63 FB(9) = 12 + 12 = 75 FB(10) = 12 + 12 = 87 FB(11) = 12 + 12 = 99 FB(12) = 12 + 12 = 111 FB(13) = 12 + 12 = 123 FB(14) = 12 + 12 = 135 FB(15) = 12 + 12 = 147 FB(16) = 12 + 12 = 159 FB(17) = 12 + 12 = 171 FB(18) = 12 + 12 = 183 FB(19) = 12 + 12 = 195 FB(20) = 12 + 12 = 207 FB(21) = 12 + 12 = 219 FB(22) = 12 + 12 = 231 FB(23) = 12 + 12 = 243 FB(24) = 12 + 12 = 255 FB(25) = 12 + 12 = 267 FB(26) = 12 + 12 = 279 FB(27) = 12 + 12 = 291 FB(28) = 12 + 12 = 303 FB(29) = 12 + 12 = 315 FB(30) = 12 + 12 = 327 运算结果:FB(30)= 327 请按任意键继续. . .
注意程序的第一行代码:
#define Detail
这是一个预处理指令,它为程序定义了一个名为Detail的标识符。而程序主方法中出现的#if-#else-#endif语句是一个条件编译语句,它根据程序是否存在标识符Detail来选择要编译的代码。如果把程序第一行的预处理器指令去掉,输出结果为:
请输入1~50之间的一个整数: 30 运算结果:FB(30)= 327 请按任意键继续. . .
2.1.1 #define和#undef指令
它们叫做标识符定义指令,分别用于定义和取消标识符,使用时后跟标识符的名称。#define和#undef必须出现在所有代码之前(不包括注释)。例如,可以在程序P18_5.CS中再插入一条#undef指令,这样就取消了#define指令结标识符Detail的定义,程序将不显示详细的计算过程:
#define Detail #undef Detail
标识符只是一个编译符号,和变量没有变量。在程序中可以定义一个变量Detail,并不会和标识符Detail产生命名冲突。
2.1.2 #if、#else、#elif和#endif指令
它们都叫做条件编译指令。在使用时,#if和#elif指令后跟标识符表达式。标识符表达式可以是单个标识符,还可以是通过与操作符“&&”以及或操作符“||”进行连接的多个标识符。
条件编译指令可以组成3种结构
(1)#if-#endif结构
(2)#if-#else-#endif结构
(3)#if-#elif-#else-#endif结构
2.2 编译警告和错误
2.2.1 #warning指令
当所在代码被编译时,#warning指令生成一条警告信息。它使用时后跟要输出的警告内容。使用C#编译器时,可以通地编译选项“/nowarn”来关闭警告信息的显示.
例如,下面的程序在编译时会生成警告信息“正在使用控制台应用程序”:
class PreprocessSample { static void Main() { #warning 正在使用控制台应用程序 // } }
2.2.2 #error指令
当所在的代码被编译时,#error指令生成一条错误信息。它使用时后跟要输出的错误内容。与#warning指令不同的是,#error指令的执行会导致编译的失败。
2.2.3 其它预处理指令
• #region和#endregion,二者配套使用,支持代码区域的折叠和展开。
• #line指令,用于指定代码所在的行数
• #pragma指令,要求编译器使用指定的指令来编译源程序
• #pragma warning指令,用于在编译时打或关闭指定的警告信息。
3 XML代码注释
如果程序中的代码注释符合特定的XML语法规则,那么在将代码编译成程序的同时,还能够根据这些注释内容生成对应的XML软件文档。
3.1 XML简介
XML的中文译法叫做可扩展标记语言(eXtensible Markup Language),是一个格式独立的,与平台和应用无关的语言。
3.2 使用XML注释
/// <summary> /// 主方法 /// </summary> static void Main(){}