惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threatpost
大猫的无限游戏
大猫的无限游戏
H
Help Net Security
P
Proofpoint News Feed
D
DataBreaches.Net
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
宝玉的分享
宝玉的分享
Recorded Future
Recorded Future
GbyAI
GbyAI
阮一峰的网络日志
阮一峰的网络日志
S
SegmentFault 最新的问题
C
Cisco Blogs
L
LangChain Blog
博客园 - 聂微东
Microsoft Security Blog
Microsoft Security Blog
Recent Announcements
Recent Announcements
量子位
AWS News Blog
AWS News Blog
爱范儿
爱范儿
Know Your Adversary
Know Your Adversary
F
Full Disclosure
MyScale Blog
MyScale Blog
Stack Overflow Blog
Stack Overflow Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Microsoft Azure Blog
Microsoft Azure Blog
S
Schneier on Security
Spread Privacy
Spread Privacy
P
Privacy International News Feed
人人都是产品经理
人人都是产品经理
V
Vulnerabilities – Threatpost
C
Cybersecurity and Infrastructure Security Agency CISA
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
美团技术团队
Latest news
Latest news
D
Docker
Scott Helme
Scott Helme
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
MongoDB | Blog
MongoDB | Blog
The Register - Security
The Register - Security
T
Tailwind CSS Blog
SecWiki News
SecWiki News
C
CERT Recently Published Vulnerability Notes
Application and Cybersecurity Blog
Application and Cybersecurity Blog
S
Security Affairs
G
GRAHAM CLULEY
Simon Willison's Weblog
Simon Willison's Weblog
Martin Fowler
Martin Fowler
I
InfoQ
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
T
Tenable Blog

博客园 - Charltsing

LoggerMessage:编写高性能的 .NET 日志 PDFImageViewer v2.1 PDF查看图像和无损导出工具,一键转换单图PDF为JBig2黑白PDF。兼谈PDF内嵌图像。 PDF的色彩空间 25 个使用.NET 10 的性能技巧 PDFontFixer v1.5 免费版,解决PDF文档复制粘贴乱码的问题,修复PDF文档字体的Unicode映射。 为什么 PDF 编辑这么难? Unicode、UTF-8、UTF-16 如何直接编辑Github的Readme.md文件 自动导入工程项目属性,Directory.Build.props 和 Directory.Build.targets 新版 C# 高效率编程指南 C# 内联数组(Inline Array):高性能数组的新选择 如何在 .NET 中使用 SIMD .NET异步编程进阶:从语法糖到高性能架构的核心突破 C# 中的安全零拷贝 使用UnsafeAccessor 访问私有字段 C# 12与.NET 8实战指南:20个提升代码质量的最佳实践 C# Net9的模块初始化器(Module Initializer) ECMA-335 CLI 规范附录 C#中避免GC压力和提高性能的8种技术 函数内联 硬件内在函数 官方:oPDF v2.1免费版,专业的PDF水印分析处理工具,无损去除水印,通杀八类PDF水印。它是PDFCommander 万能PDF水印删除工具的升级版。 C#自动引用Debug | Release版本的dll
C# 预处理器指令语言 - 完整语法速查
Charltsing · 2026-06-19 · via 博客园 - Charltsing

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

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 — 调试校验和

主要为 http://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

八、指令不重叠关系速查

指令类别涵盖指令
​#region ​#region 完整嵌套
​#if ​#if 完整嵌套
​#region ​#if 一个完整包含另一个
​#region ​#if 不能重叠/交错

最后

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