

























作者:k'ai
链接:https://zhuanlan.zhihu.com/p/2050983681922274149
C# 预处理器指令的完整语言参考,涵盖所有指令的精确语法、使用规则和边界条件。与前一篇教程风格的文章不同,这篇侧重语法规范和细节,适合需要精确查阅特定指令的开发者。
核心前提: C# 的预处理器指令没有独立的预处理器,由编译器直接处理。指令必须独占一行。不能像 C/C++ 那样创建宏,条件编译仅判断符号是否定义(布尔逻辑)。
| 指令类别 | 涵盖指令 |
|---|---|
| Nullable 上下文 | #nullable enable/disable/restore及 annotations/warnings 子集 |
| 条件编译 | #if、#elif、#else、#endif |
| 符号定义 | #define、#undef |
| 代码区域 | #region、#endregion |
| 诊断信息 | #error、#warning、#line |
| 编译器指令 | #pragma warning、#pragma checksum |
| 文件应用 | #!(shebang)、#: |
#nullable — 可为空上下文控制可为 null 引用类型的注释和警告。指令优先级高于项目设置,效果持续到下一指令或文件结束。
| 指令类别 | 涵盖指令 |
|---|---|
| #nullable disable | 禁用可为空上下文(注释 + 警告) |
| #nullable enable | 启用可为空上下文 |
| #nullable restore | 恢复为项目级别的设置 |
可以单独控制注释(annotations,即 ? 标记)和警告(warnings):
| 指令类别 | 涵盖指令 |
|---|---|
| #nullable disable annotations | 禁用?标记(不检查 nullability 注释) |
| #nullable enable annotations | 启用?标记 |
| #nullable restore annotations | 恢复注释设置为项目级别 |
| #nullable disable warnings | 禁用可为 null 警告 |
| #nullable enable warnings | 启用可为 null 警告 |
| #nullable restore warnings | 恢复警告设置为项目级别 |
使用场景: 在一个项目逐步迁移到 nullable aware 时,可以局部启用或禁用特定文件/区域。
// 整个文件默认启用 nullable
#nullable enable
public string? GetOptional() => null; // string? 有效,返回 null 不报警告
#nullable disable warnings
public string GetRequired() => null; // 返回 null 不报警告(warnings 已禁用)
#nullable restore warnings
// 后面的代码恢复警告
#if / #elif / #else / #endif #if SYMBOL
// 当 SYMBOL 已定义时编译
#elif OTHER_SYMBOL
// 当 OTHER_SYMBOL 已定义且前面条件不满足时编译
#else
// 前面所有条件都不满足时编译
#endif
| 指令类别 | 涵盖指令 | |
|---|---|---|
| ! | 逻辑非 | #if !DEBUG |
| == | 相等 | #if NET8_0 == true |
| != | 不等 | #if NET8_0 != true |
| && | 逻辑与 | #if DEBUG && NET10_0 |
| || | 逻辑或 | #if DEBUG || TRACE |
| () | 分组 | #if (DEBUG || TRACE) && !PRODUCTION |
SDK 风格项目根据目标框架自动定义一组符号:
桌面 / 框架相关:
| 指令类别 | 涵盖指令 | |
|---|---|---|
| .NET Framework 4.7.2 | NET472 | NET472_OR_GREATER |
| .NET Framework 4.8 | NET48 | NET48_OR_GREATER |
| .NET Standard 2.1 | NETSTANDARD2_1 | NETSTANDARD2_1_OR_GREATER |
现代 .NET(.NET 5+):
| 指令类别 | 涵盖指令 | |
|---|---|---|
| .NET 8 | NET8_0 | NET8_0_OR_GREATER |
| .NET 9 | NET9_0 | NET9_0_OR_GREATER |
| .NET 10 | NET10_0 | NET10_0_OR_GREATER |
平台符号:
| 指令类别 | 涵盖指令 | |
|---|---|---|
| Android | ANDROID | ANDROID35_0_OR_GREATER |
| iOS | IOS | IOS15_1_OR_GREATER |
| Windows | WINDOWS | WINDOWS10_0_17763_0_OR_GREATER |
通用符号:
| 指令类别 | 涵盖指令 |
|---|---|
| DEBUG | Debug 生成配置自动定义 |
| TRACE | Trace 常量,默认开启 |
public static string DownloadContent(string url)
{
#if NET40
WebClient _client = new WebClient();
return _client.DownloadString(url);
#else
HttpClient _client = new HttpClient();
return _client.GetStringAsync(url).Result;
#endif
}
#define / #undef #define MYTEST // 定义符号
#undef MYTEST // 取消定义
| 指令类别 | 涵盖指令 |
|---|---|
| 位置 | 必须在文件最开头,在所有非预处理器代码之前 |
| 作用域 | 从定义位置到文件末尾 |
| 不能赋值 | #define MYTEST 1是非法的,只能声明符号名 |
| 常量应用 | 需要常量值用const,不要用#define |
| 编译器选项 | 也可通过DefineConstants属性全局定义 |
// 正确 ✅
#define FEATURE_EXPERIMENTAL
// 错误 ❌
#define FEATURE_EXPERIMENTAL true // 编译错误
#region / #endregion — 代码折叠 ⭐ #region 区域名称
// ... 可折叠的代码 ...
#endregion
| 指令类别 | 涵盖指令 |
|---|---|
| 配对 | #region必须由#endregion终止 |
| 嵌套 | #region内可以嵌套另一个#region |
| 与#if的关系 | 不能重叠,但可互相嵌套:#region内含#if块,或#if内含#region |
| 编译影响 | 不影响编译,纯 IDE 大纲视图特性 |
// ✅ 正确 — #if 完整包含在 #region 内
#region 多框架适配
#if NET8_0_OR_GREATER
Console.WriteLine(".NET 8+");
#endif
#endregion
// ✅ 正确 — #region 完整包含在 #if 内
#if DEBUG
#region 调试工具
Console.WriteLine("Debug tools active");
#endregion
#endif
// ❌ 错误 — 重叠(编译失败)
#region 开始
#if DEBUG
#endregion // ❌ 在 #if 没有 #endif 之前关闭 #region
#endif
#region MyClass definition
public class MyClass
{
static void Main()
{
}
}
#endregion
常见坑:
#region 不能拆分#if/#endif 块。如果你在一个#region 内打开了#if,必须在同一个#region 内用#endif关闭。
#error、#warning、#line#error — 主动生成编译错误 #error Deprecated code in this method.
// 编译错误 CS1029: #error: 'Deprecated code in this method.'
常用场景: 在不支持的条件下阻止编译:
#if !NET8_0_OR_GREATER
#error This library requires .NET 8 or later.
#endif
#warning — 主动生成编译警告 #warning Deprecated code in this method.
// 编译警告 CS1030: #warning: 'Deprecated code in this method.'
#line — 修改编译输出行号/文件名| 指令类别 | 涵盖指令 |
|---|---|
| #line 200 "Special.cs" | 强制编译器以第 200 行、"Special.cs" 文件名报告后续代码 |
| #line default | 恢复默认行号 |
| #line hidden | 对调试器隐藏后续行(逐步执行时跳过) |
C# 高级语法(适用于 DSL / 代码生成器):
#line (起始行, 起始列) - (结束行, 结束列) 列偏移 "原始源文件名"
常用场景:Razor 页面将生成的 .cs 文件中警告/错误映射回 .cshtml 原始源代码。
#pragma 指令#pragma warning — 警告控制 #pragma warning disable 414, CS3021 // 从下一行起禁用指定警告
// ... 受抑制的代码 ...
#pragma warning restore CS3021 // 恢复指定警告
格式控制:
#pragma warning disable format // 禁用代码格式化(如 Ctrl+K,D)
// ... 你想保留手动格式的代码 ...
#pragma warning restore format // 恢复格式化
#pragma checksum — 调试校验和主要为 http://ASP.NET 页面设计,确保调试器找到正确的源文件:
#pragma checksum "file.cs" "{3673e4ca-6098-4ec1-890f-8fceb2a794a2}" "hex-bytes..."
参数:
#! 和 #: — 基于文件的应用#! — Shebang #!/usr/bin/env dotnet
Console.WriteLine("Hello");
Unix 下配合 chmod +x 可直接执行。
#: — 文件级配置C# 编译器忽略 #: 开头的行,但它们被 .NET SDK 等工具按约定解析:
#:package Spectre.Console@*
#:sdk Microsoft.NET.Sdk.Web
#:property PublishAot=false
| 指令类别 | 涵盖指令 | ||
|---|---|---|---|
| #region | #region | ✅ | 完整嵌套 |
| #if | #if | ✅ | 完整嵌套 |
| #region | #if | ✅ | 一个完整包含另一个 |
| #region | #if | ❌ | 不能重叠/交错 |
这份参考文档覆盖了 C# 预处理器指令的全部语法规范。日常开发中最常用的就三类:#if DEBUG、#nullable enable、#region。其余的(#line 高级映射、#pragma checksum)主要在代码生成器和 ASP.NET 底层框架中使用。记住最核心的一条:C# 预处理指令没有宏,条件编译只判断符号有没有定义,不要把它当成 C/C++ 的 #define 宏来用。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。