预处理指令
预处理指令是C/C++中比较常用的概念,C#编译器也定义了一部分预处理指令,用于在编译前向编译器传递一些指示,比如根据编译符号选择性地编译某段代码,其用法与C/C++类似。不过要注意,C#的预处理指令并不是C/C++那种文本替换式的宏,它不支持#include,#define也仅仅是定义一个编译符号,并不会做文本替换。这部分内容在实际开发中只有特定几个场景较为常用,这篇笔记我们简单介绍一下。
| 指令 | 说明 |
|---|---|
| #define identifier | 定义编译符号 |
| #undef identifier | 取消定义编译符号 |
| #if expression | 如果表达式为true,则编译下面片段 |
| #elif expression | 如果表达式为true,则编译下面片段 |
| #else | 如果之前#if和#elif表达式均为false,则编译下面片段 |
| #endif | 标记结束#if结构 |
| #region name | 标记一段代码开始 |
| #endregion name | 标记一段代码结束 |
| #warning message | 显示编译时警告信息 |
| #error message | 显示编译时错误信息 |
| #line indicator | 修改编译器信息中显示的行数 |
| #nullable enable | 开启可空引用类型的静态检查(C# 8.0) |
| #pragma warning | 提供修改编译器警告信息行为的选项 |
注:#define和#undef必须出现在文件的开头,即任何代码(包括using和namespace)之前,否则会编译报错。
条件编译
预处理指令最常见的用途是条件编译,即根据是否定义了某个编译符号来决定是否编译某段代码。除了使用#define在源码中定义编译符号,更常见的做法是在工程文件中通过<DefineConstants>配置符号,这样可以为不同环境(如Debug、Release)定义不同的符号。
#define SHOW_DEBUG_INFO
class Program
{
static void Main()
{
#if SHOW_DEBUG_INFO
Console.WriteLine("调试模式");
#else
Console.WriteLine("发布模式");
#endif
}
}
#if后面的表达式可以使用!、&&、||、==、!=等运算符以及true、false,与编译符号组合进行判断。
#region 代码折叠
#region和#endregion用于在IDE中将一段代码折叠起来,它只在编辑器中起作用,并不会影响编译结果,常用于将一个大文件按功能分区以便阅读。
#region 字段声明
private int _a;
private int _b;
#endregion
#warning 和 #error
#warning和#error用于在编译时主动产生警告或错误信息,常配合条件编译使用,例如提醒某段代码尚未完成或某个版本不应被发布。
#if !RELEASE
#warning 当前为开发版本,请勿发布
#endif
#nullable
C# 8.0引入了可空引用类型,默认情况下是关闭的,除了可以在工程文件中通过<Nullable>enable</Nullable>全局开启,也可以在源码文件中使用#nullable指令按文件或按片段开启、关闭。有关可空引用类型可参考数据类型章节。
#nullable enable
string? name = null;
此外,#nullable disable可以关闭检查,#nullable restore可以恢复为工程默认设置。