C# 语言的预处理器指令

时间:2022-10-01 01:12:44

C# 语言的预处理器指令:

  • #if
  • #else
  • #elif
  • #endif
  • # define
  • #undef
  • #warning
  • #error
  • #line
  • #region
  • #endregion
  • #pragma
  • #pragma warning
  • #pragma checksum

一、#if
#if 使您可以开始条件指令,测试一个或多个符号以查看它们是否计算为 true。如果它们的计算结果确实为 true,则编译器将计算位于 #if 与最近的 #endif 指令之间的所有代码。以 #if 指令开始的条件指令必须用 #endif 指令显式终止。例如:


#define DEBUG
// ...
#if DEBUG
Console.WriteLine("Debug version");
#endif


可以使用运算符 ==(相等)、!=(不相等)、&&(与)及 ||(或)来计算多个符号。还可以用括号将符号和运算符分组。


备注:
使用 #if 以及 #else、#elif、#endif、#define 和 #undef 指令,可以包括或排除基于由一个或多个符号组成的条件的代码。这在编译调试版本的代码或编译特定配置时最为有用。

二、#else
#else 允许您创建复合条件指令,因此,如果前面的 #if 或(可选)#elif 指令中的任何表达式都不为 true,则编译器将计算 #else 与后面的 #endif 之间的所有代码。

备注:#endif必须是#else的下一个预处理器指令。

三、#elif

使您得以创建复合条件指令。如果前面的 #if 和前面的任何 #elif(可选)指令表达式的计算结果都不是 true,则将计算 #elif 表达式。如果 #elif 表达式计算为 true,编译器将计算位于 #elif 和下一个条件指令之间的所有代码。

三、#endif
#endif 指定以 #if 指令开头的条件指令的结尾。

四、#define
使用 #define 可以定义一个符号,并通过将该符号用作表达式传递给 #if 指令,使该表达式的计算结果为 true。可以定义符号,但是无法对符号赋值。例如:

# define DEBUG

符号可用于指定编译的条件。可以使用 #if 或 #elif 来测试符号。还可以使用 conditional 属性执行条件编译。
也可以用 /define 编译器选项来定义符号。可以用 #undef 来取消定义符号。 用 #define 创建的符号的范围是在其中定义该符号的文件。

备注:与C/C++不一样,C#的#define语句仅允许你在符号表中插一个标签,但你不能给任何一个符号赋一个值。与C/C++另一个不同的地方,C/C++允许#define预处理器指令出现在任何所需的地方,而在使用任何非预处理器指令的指令前,C#的#define指令必须出现在一个文件中。用#define定义的标签不会与同名的变量冲突。这就是说,一个变量名不应该传递给预处理器指令,而且符号只能通过预处理器指令来求值。插入到符号表中的标签的作用域就是定义它的文件。可以用#undef取消符号定义。

五、#undef
#undef 使您可以取消符号的定义,以便通过将该符号用作 #if 指令中的表达式,使表达式的计算结果为 false。

六、#warning
#warning 使您得以从代码的特定位置生成一级警告。例如:
#warning Deprecated code in this method.


备注:
#warning 通常用在条件指令中。也可以用 #error(C# 参考)生成用户定义的错误。
示例
// preprocessor_warning.cs
// CS1030 expected
#define DEBUG
class MainClass
{
static void Main()
{
#if DEBUG
#warning DEBUG is defined
#endif
}
}

七、#error
#error 使您可以从代码中的特定位置生成错误。例如:
#error Deprecated code in this method.

八、#line
#line 使您可以修改编译器的行号以及(可选)错误和警告的文件名输出。下面的示例说明如何报告与行号关联的两个警告。#line 200 指令强迫行号为 200(尽管默认值为 #7)。另一行 (#9) 作为默认 #line 指令的结果跟在通常序列后。


class MainClass
{
static void Main()
{
#line 200
int i; // CS0168 on line 200
#line default
char c; // CS0168 on line 9
}
}


备注:
#line 指令可能由生成过程中的自动中间步骤使用。例如,如果行从原始的源代码文件中移除,但是您仍希望编译器基于文件中的原始行号生成输出,则可以移除行,然后用 #line 模拟原始行号。 #line hidden 指令对调试器隐藏若干连续的行,这样当开发人员在逐句通过代码时,将会跳过 #line hidden 和下一个 #line 指令(假定它不是另一个 #line hidden 指令)之间的所有行。此选项也可用来使 ASP.NET 能够区分用户定义的代码和计算机生成的代码。尽管 ASP.NET 是此功能的主要使用者,但很可能将有更多的源生成器使用它。
#line hidden 指令不会影响错误报告中的文件名或行号。即,如果在隐藏块中遇到错误,编译器将报告当前文件名和错误的行号。
#line filename 指令指定您希望出现在编译器输出中的文件名。默认情况下,使用源代码文件的实际名称。文件名必须括在双引号 ("") 中。 源代码文件可以具有 #line 指令的任何编号。


示例
下面的示例说明调试器如何忽略代码中的隐藏行。运行此示例时,它将显示三行文本。但是,当设置如示例所示的断点并按 F10 键逐句通过代码时,您将看到调试器忽略了隐藏行。还请注意,即使在隐藏行上设置断点,调试器仍会忽略它。
// preprocessor_linehidden.cs
using System;
class MainClass
{
static void Main()
{
Console.WriteLine("Normal line #1."); //这里设置断点
#line hidden
Console.WriteLine("Hidden line.");
#line default
Console.WriteLine("Normal line #2.");
}
}

九、#region
#region 使您可以在使用 Visual Studio 代码编辑器的大纲显示功能时指定可展开或折叠的代码块。例如:
#region MyClass definition
public class MyClass
{
static void Main()
{
}
}
#endregion


备注:
#region 块必须以 #endregion 指令终止。
#region 块不能与 #if 块重叠。但是,可以将 #region 块嵌套在 #if 块内,或将 #if 块嵌套在 #region 块内。

十、#endregion
#endregion 标记 #region 块的结尾。

十一、#pragma
#pragma 用于给编辑器提供特殊的指令,说明如何编译包含杂注的文件。
#pragma pragma-name pragma-arguments
参数
pragma-name
可识别杂注的名称。
pragma-arguments
杂注特定的参数。

十二、#pragma warning
#pragma warning 可用于启用或禁用某些警告。
#pragma warning disable warning-list
#pragma warning restore warning-list
参数
warning-list
警告编号的逗号分隔列表。只输入数字,不包括前缀 "CS"。
当没有指定警告编号时,disable 禁用所有警告,而 restore 启用所有警告。
示例
// pragma_warning.cs
using System;

#pragma warning disable 414, 3021
[CLSCompliant(false)]
public class C
{
int i = 1;
static void Main()
{
}
}
#pragma warning restore 3021
[CLSCompliant(false)] // CS3021
public class D
{
int i = 1;
public static void F()
{
}
}

十三、#pragma checksum
可用于生成源文件的校验和,以帮助调试 ASP.NET 页。
#pragma checksum "filename" "{guid}" "checksum bytes"
参数
"filename"
要求监视更改或更新的文件的名称。
"{guid}"
文件的全局唯一标识符 (GUID)。
"checksum_bytes"
十六进制数的字符串,表示校验和的字节。必须是偶数位的十六进制数。奇数位的数字会导致编译时警告,从而使指令被忽略。
备注:
Visual Studio 调试器使用校验和来确保找到的总是正确的源。编译器计算源文件的校验和,然后将输出发出到程序数据库 (PDB) 文件。最后,调试器使用 PDB 来比较它为源文件计算的校验和。
此解决方案不适用于 ASP.NET 项目,因为算出的是生成的源文件而不是 .aspx 文件的校验和。为解决此问题,#pragma checksum 为 ASP.NET 页提供了校验和支持。
在 Visual C# 中创建 ASP.NET 项目时,生成的源文件包含 .aspx 文件(从该文件生成源文件)的校验和。然后,编译器将此信息写入 PDB 文件。
如果编译器在该文件中没有遇到 #pragma checksum 指令,它将计算校验和,然后将算出的值写入 PDB 文件。
示例
class TestClass
{
static int Main()
{
#pragma checksum "file.cs" "{3673e4ca-6098-4ec1-890f-8fceb2a794a2}" "{012345678AB}" // New checksum
}
}