首页 > 编程开发 > C类语言    日期:2026-06-20 / 浏览

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 引用类型的注释和警告。指令优先级高于项目设置,效果持续到下一指令或文件结束。

1.1 基本形式

指令 效果
​#nullable disable 禁用可为空上下文(注释 + 警告)
​#nullable enable 启用可为空上下文
​#nullable restore 恢复为项目级别的设置

1.2 细粒度控制

可以单独控制注释(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

2.1 基本语法

 #if SYMBOL
     // 当 SYMBOL 已定义时编译
 #elif OTHER_SYMBOL
     // 当 OTHER_SYMBOL 已定义且前面条件不满足时编译
 #else
     // 前面所有条件都不满足时编译
 #endif

2.2 支持的运算符

运算符 含义 示例
​! 逻辑非 ​#if !DEBUG
​== 相等 ​#if NET8_0 == true
​!= 不等 ​#if NET8_0 != true
​&& 逻辑与 ​#if DEBUG && NET10_0
​|| 逻辑或 ​#if DEBUG || TRACE
​() 分组 ​#if (DEBUG || TRACE) && !PRODUCTION

2.3 预定义符号

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 常量,默认开启

2.4 多框架适配示例

 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

3.1 语法

 #define MYTEST         // 定义符号
 #undef MYTEST         // 取消定义

3.2 关键规则

规则 说明
位置 必须在文件最开头,在所有非预处理器代码之前
作用域 从定义位置到文件末尾
不能赋值 ​#define MYTEST 1​是非法的,只能声明符号名
常量应用 需要常量值用const​,不要用#define
编译器选项 也可通过DefineConstants属性全局定义
 // 正确 ✅
 #define FEATURE_EXPERIMENTAL
 ​
 // 错误 ❌
 #define FEATURE_EXPERIMENTAL true   // 编译错误

四、#region​ /#endregion— 代码折叠 ⭐

4.1 语法

 #region 区域名称
 // ... 可折叠的代码 ...
 #endregion

4.2 规则

规则 说明
配对 ​#region​必须由#endregion终止
嵌套 ​#region​内可以嵌套另一个#region
与#if的关系 不能重叠,但可互相嵌套:#region​内含#if​块,或#if​内含#region
编译影响 不影响编译,纯 IDE 大纲视图特性

4.3 正确 vs 错误的嵌套

 // ✅ 正确 — #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

4.4 示例

 #region MyClass definition
 public class MyClass
 {
     static void Main()
     {
     }
 }
 #endregion

常见坑: #region​ 不能拆分 #if​/#endif​ 块。如果你在一个 #region​ 内打开了 #if​,必须在同一个 #region​ 内用 #endif 关闭。

五、诊断指令 —#error​、#warning​、#line

5.1#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

5.2#warning— 主动生成编译警告

 #warning Deprecated code in this method.
 // 编译警告 CS1030: #warning: 'Deprecated code in this method.'

5.3#line— 修改编译输出行号/文件名

指令 效果
​#line 200 "Special.cs" 强制编译器以第 200 行、"Special.cs" 文件名报告后续代码
​#line default 恢复默认行号
​#line hidden 对调试器隐藏后续行(逐步执行时跳过)

C# 高级语法(适用于 DSL / 代码生成器):

 #line (起始行, 起始列) - (结束行, 结束列) 列偏移 "原始源文件名"

常用场景:Razor 页面将生成的 .cs​ 文件中警告/错误映射回 .cshtml 原始源代码。

六、#pragma指令

6.1#pragma warning— 警告控制

 #pragma warning disable 414, CS3021   // 从下一行起禁用指定警告
 // ... 受抑制的代码 ...
 #pragma warning restore CS3021        // 恢复指定警告

格式控制:

 #pragma warning disable format   // 禁用代码格式化(如 Ctrl+K,D)
 // ... 你想保留手动格式的代码 ...
 #pragma warning restore format   // 恢复格式化

6.2#pragma checksum— 调试校验和

主要为 ASP.NET 页面设计,确保调试器找到正确的源文件:

 #pragma checksum "file.cs" "{3673e4ca-6098-4ec1-890f-8fceb2a794a2}" "hex-bytes..."

参数:

  1. 文件名 — 源文件路径
  2. GUID — 文件的唯一标识
  3. 校验和字节 — 十六进制字符串

七、#!​ 和#:— 基于文件的应用

7.1#!— Shebang

 #!/usr/bin/env dotnet
 Console.WriteLine("Hello");

Unix 下配合 chmod +x 可直接执行。

7.2#:— 文件级配置

C# 编译器忽略 #: 开头的行,但它们被 .NET SDK 等工具按约定解析:

 #:package Spectre.Console@*
 #:sdk Microsoft.NET.Sdk.Web
 #:property PublishAot=false

八、指令不重叠关系速查

结构 A 结构 B 可以嵌套? 条件
​#region ​#region 完整嵌套
​#if ​#if 完整嵌套
​#region ​#if 一个完整包含另一个
​#region ​#if 不能重叠/交错

最后

这份参考文档覆盖了 C# 预处理器指令的全部语法规范。日常开发中最常用的就三类:#if DEBUG​、#nullable enable​、#region​。其余的(#line​ 高级映射、#pragma checksum​)主要在代码生成器和 ASP.NET 底层框架中使用。记住最核心的一条:C# 预处理指令没有宏,条件编译只判断符号有没有定义,不要把它当成 C/C++ 的 #define 宏来用。

觉得上面的内容有用吗?快来点个赞吧!

点赞() 我要打赏

温馨提示 : 本站内容来自会员投稿以及互联网,所有源码及教程均为作者总结编辑,请大家在使用过程中提前做好备份,以免发生无法预知的错误,源码类教程请勿直接用于生产环境!

 可能感兴趣的文章