23.1 什么是预处理指令
源代码指定了程序的定义。预处理指令(preprocessor directive)指示编译器如何处理源代码。例如,在某些情况下,我们可能希望编译器忽略一部分代码;而在其他情况下,我们可能希望代码被编译。参见处理指令给了我们这样的选项。
在C和C++中有实际的预处理阶段,此时预处理程序遍历源代码并且为之后的编译阶段准备文本输出流,在C#中没有实际的预处理程序。“预处理”指令由编译器来处理,而这个术语保留了下来。
23.2 基本规则
下面是预处理指令的最重要的一些语法规则:
- 预处理指令必须和C#代码在不同的行。
- 与C#语句不同,预处理指令不需要以分号结尾。
- 包含预处理指令的每一行必须以#字符开始。
- 在#字符前可以有空格。
- 在#字符和指令之间可以有空格。
- 允许行尾注释
- 在预处理指令所在的行不允许分隔符注释。
C#预处理指令
预处理指令 指令 含义概要 #define identifier 定义编译符 #undef identifier 取消定义编译符 #if expression 如果表达式是true,编译下面的片段 #leif expression 如果表达式是true,编译下面的片段 #else 如果之前的#if或#elif表达式是false,编译下面的片段 #endif 标记为一个#if结构的结束 #region name 标记一段代码的开始,没有编译效果 #endregion name 标记一段代码的结束,没有编译效果 #warning message 显示编译时的警告信息 #error message 显示编译时的错误信息 #line indicator 修改在编译器消息中显示的行数 #pragma text 指定有关程序上下文的信息
23.3 #define和#under指令
编译符号是只有两种状态的标识符,要么被定义,要么未被定义。编译符号有如下的特性:
- 它可以是除了true或false以外的任何标识符,包括C#关键词,以及在C#代码中声明的标识符,这两者都是可以的。
- 它没有值。与C和C++不同,它不表示字符串。
#define指令声明一个编译符号。
#undef指令取消定义一个编译符号。
#define和#undef指令只能在源文件的第一行,也就是任何C#代码之前使用。在C#代码开始后,#define和#undef指令就不能再使用。
编译符号的范围被限制于单个源文件。只要编译符号在任何C#代码之前,重复定义已存在的编译符号也是允许的。
23.4 条件编译
条件编译允许我们根据某个编译符号是否被定义标注一段代码被编译或跳过。
有四个指令可以用来指定条件编译:
- #if
- #else
- #elif
- #endif
条件是一个返回true或false的简单表达式。
在#if和#elif指令中使用的条件 参数类型 意义 运算结果 编译符号 使用#define指令(未)定义的标识符 True:如果符号已经使用#define指令这下 义。
False:其他表达式 使用符号和操作符 !、==、!=、$$、||构建的 True:如果其表达式运算结果为true
false:其他
23.5 条件编译结构
#if和#endif在条件编译结构中需要配对使用。只要有#if指令,就必须有配对的#endif。
#if和#if....#else结构:
- 如果#if结构中的条件运算结果为true,随后的代码段就会被编译,否则就会被跳过。
- 在#if....#else结构中,如果条件运算结果为true,if和else之间的代码块会被编译;否则,else和endif之间的代码块会被编译。
23.6 诊断指令
诊断指令产生用户自定义的编译时警告或错误消息。
下面是诊断指令的语法。Message是字符串,但是需要注意,与普通的C#字符串不同,它们不需要被引号包围。
当编译器遇到诊断指令时,它会输出相关的消息。诊断指令的消息会和任何编译器产生的警告和错误消息列在一起。#warning Message
#error Message
例如,如下代码是展示了一个#error指令和一个#warning指令。
- #error指令在#if结构中,因此只有符合#if指令的条件时才会生成消息。
- #warning指令用于提醒程序员回 头来清理这段代码。
#define RightHanded #define LeftHanded #if RightHanded && LeftHanded #error Can't build for both RightHanded and LeftHanded #endif #warning Remember to come back and clean up this code!
23.7 行号指令
行号指令可以做很多事情,诸如:
- 改变由编译器警告和错误消息报告的出现行数。
- 改变被让编译源文件的文件名。
- 对交互式调试器隐藏一些行。
#line指令的语法如下:
#line integer //设置下一行值为整数的行的行号。#line "filename" //设置文件名#line default //重新保存实际的行号和文件名#line hidden //在断点调试器中隐藏代码#line //停止在调试器中隐藏代码。
#line指令加上一个整数参数会使编译器认为下面代码的行是所设置的行,之后的行数会根据这行的数字继续。
- 要改变外观文件名,可以在双引号内使用文件名作为参数。双引号是必须的。
- 要返回真实行号和真实文件名,可以使用default参数。
- 要对交互调试器的断点调试功能隐藏代码段,可以使用hidden作为参数。要停止参数,可以使用不带任何参数的指令。到目前为止,这个功能大多用于在ASP.NET中隐藏编译器生成的代码。
23.8 区域指令
区域指令允许我们标注和有选择性地命名一段代码。#region指令:
- 被放置在希望标的代码段之上。
- 用指令后的可选字符串文本作为其名字。
- 在之后的代码中必须由#endregion指令终止。
尽管区域指令被编译器忽略,但它们可以被源代码工具所使用。例如,Visual Studio允许我们很简单地隐藏或显示区域。
23.9 #pragma warning指令
#pragma warning指令允许我们关闭或重新开启警告信息。
- 要关闭警告消息,可以使用disable加上逗号分隔的希望产有意者的警告数列表的形式。
- 要重新开启警告消息,可以使用restore加上逗号分隔的希望关闭的警告数列表的形式。
例如,下面的代码关闭了两个警告消息:618和414.在后面的代码中又开启了618警告消息,但还是保持414消息为关闭状态。
#pragma warning disable 618,414
#pragma warning restore 618
如果使用任一种不带警告数字列表的形式,这个命令会应用于所有警告。
#pragma warning disable //所有警告消息在这段代码中处于关闭状态
#pragma warning restore //所有警告消息在这段代码处于开启状态