C#预处理器指令(Preprocessor Directives)是以#开头的特殊命令,它们不会生成 IL(中间语言)代码,而是在编译之前由编译器处理,用来控制代码是否参与编译、定义符号、输出警告等。
与 C/C++ 不同,C# 的预处理器不能进行宏替换(没有
#define MAX 100这种功能)。
一、什么是预处理器指令?
例如:
#define DEBUG using System; class Program { static void Main() { #if DEBUG Console.WriteLine("调试模式"); #endif Console.WriteLine("程序结束"); } }输出:
调试模式 程序结束如果没有定义DEBUG
// #define DEBUG那么:
#if DEBUG Console.WriteLine("调试模式"); #endif整个代码块不会参与编译。
二、C# 常见预处理器指令
| 指令 | 作用 |
|---|---|
| #define | 定义编译符号 |
| #undef | 取消定义 |
| #if | 条件编译 |
| #elif | 否则如果 |
| #else | 否则 |
| #endif | 结束条件编译 |
| #warning | 生成编译警告 |
| #error | 生成编译错误 |
| #region | 折叠代码区域 |
| #endregion | 结束区域 |
| #line | 修改行号信息 |
| #nullable | 控制可空引用类型 |
| #pragma | 控制编译器行为 |
三、#define
用于定义一个编译符号。
语法:
#define 名称例如:
#define TEST using System; class Program { static void Main() { #if TEST Console.WriteLine("测试模式"); #endif } }输出:
测试模式注意:
- 必须放在文件最顶部
- 不能放到 namespace 内
- 一个文件定义仅影响当前文件
错误示例:
using System; #define TEST编译错误。
四、#undef
取消定义。
例如:
#define TEST #undef TEST using System; class Program { static void Main() { #if TEST Console.WriteLine("测试"); #else Console.WriteLine("正式"); #endif } }输出:
正式五、#if 条件编译
最常见。
例如:
#define DEBUG using System; class Program { static void Main() { #if DEBUG Console.WriteLine("Debug版本"); #endif #if RELEASE Console.WriteLine("Release版本"); #endif } }输出:
Debug版本多条件
可以使用:
#if DEBUG && TEST例如:
#define DEBUG #define TEST #if DEBUG && TEST Console.WriteLine("两个都存在"); #endif也支持:
#if DEBUG || TEST以及
#if !DEBUG六、#elif
例如:
#define WINDOWS #if LINUX Console.WriteLine("Linux"); #elif WINDOWS Console.WriteLine("Windows"); #else Console.WriteLine("Unknown"); #endif输出:
Windows七、#else
例如:
#if DEBUG Console.WriteLine("调试"); #else Console.WriteLine("发布"); #endif八、#endif
结束一个条件块。
例如:
#if DEBUG ... #endif九、#warning
人为生成一个编译警告。
例如:
#warning 这里还有BugVisual Studio:
warning CS1030: 这里还有Bug程序仍然可以运行。
十、#error
人为生成编译错误。
例如:
#error 当前版本禁止发布结果:
error CS1029: 当前版本禁止发布程序无法编译。
适合:
- 禁止旧版本
- 禁止错误配置
- 提醒开发人员
十一、#region
用于代码折叠。
例如:
#region 字段 private int age; private string name; #endregionIDE 中显示:
▶ 字段点击即可展开。
也可以:
#region 属性 ... #endregion #region 方法 ... #endregion大型项目大量使用。
十二、#line
修改编译器认为的行号。
例如:
#line 100 Console.WriteLine("Hello");如果这里报错:
第100行而不是实际行号。
也可以:
#line hidden隐藏行号。
恢复:
#line default通常用于:
- 代码生成器
- Razor
- T4 模板
十三、#nullable(C# 8.0+)
控制可空引用类型。
开启:
#nullable enable string? name = null;关闭:
#nullable disable恢复默认:
#nullable restore例如:
#nullable enable string name = null;编译器:
警告: 不能赋值 null而:
string? name = null;没有警告。
十四、#pragma
控制编译器行为。
最常见:
#pragma warning disable例如:
#pragma warning disable CS0168 int a; #pragma warning restore CS0168表示:
关闭:
变量声明但未使用之后恢复。
也可以:
#pragma warning disable CS0618关闭:
过时 API十五、实际开发中的应用
1. Debug 日志
#if DEBUG Console.WriteLine("开始执行"); #endifRelease 不会编译。
2. 平台区分
#if WINDOWS ... #elif LINUX ... #endif3. 测试代码
#if TEST RunUnitTest(); #endif上线自动去除。
4. API 版本控制
#if NET8_0 ... #elif NET6_0 ... #endif.NET SDK 会自动定义目标框架对应的编译符号。
5. 临时屏蔽警告
#pragma warning disable CS0618 OldMethod(); #pragma warning restore CS0618十六、注意事项
❌ 错误示例:
#define MAX 100C# 不支持宏替换。
只能:
#define DEBUG不能:
#define Version "1.0"十七、与 C/C++ 的区别
| 功能 | C# | C/C++ |
|---|---|---|
| 宏替换 | ❌ 不支持 | ✅ 支持 |
| 条件编译 | ✅ 支持 | ✅ 支持 |
包含文件#include | ❌ 不支持 | ✅ 支持 |
| 定义符号 | ✅ 支持 | ✅ 支持 |
| 代码折叠 | ✅#region | 部分编辑器支持 |
| 生成警告 | ✅ | ✅ |
十八、常用预处理器指令速查
| 指令 | 说明 | 是否常用 |
|---|---|---|
#define | 定义编译符号 | ⭐⭐⭐⭐⭐ |
#undef | 取消定义 | ⭐⭐⭐ |
#if | 条件编译 | ⭐⭐⭐⭐⭐ |
#elif | 条件分支 | ⭐⭐⭐⭐ |
#else | 条件分支 | ⭐⭐⭐⭐ |
#endif | 结束条件编译 | ⭐⭐⭐⭐⭐ |
#warning | 生成编译警告 | ⭐⭐⭐ |
#error | 生成编译错误 | ⭐⭐⭐ |
#region | 代码折叠 | ⭐⭐⭐⭐⭐ |
#endregion | 结束代码区域 | ⭐⭐⭐⭐⭐ |
#line | 修改行号信息 | ⭐⭐ |
#nullable | 控制可空引用类型 | ⭐⭐⭐⭐ |
#pragma | 控制编译器行为 | ⭐⭐⭐⭐ |
总结
预处理器指令的主要用途是在编译阶段控制代码,而不是在程序运行时改变逻辑。其中,日常开发最常用的是:
#if / #elif / #else / #endif:根据编译符号实现条件编译。#region / #endregion:组织和折叠代码,提高可读性。#nullable:启用或控制可空引用类型检查,减少空引用异常。#pragma warning:临时关闭或恢复特定编译警告。
掌握这些指令后,你可以更灵活地管理不同编译配置(如 Debug/Release)、跨平台代码以及大型项目的代码结构。