预处理器指令是编译前由预处理器处理的以#开头的指令,不参与运行时执行,不影响il或程序集;其作用域限于当前源文件,必须位于所有可执行代码之前,仅支持符号存在性判断(如#if debug),不支持c#表达式运算。

预处理器指令不是运行时代码,它们在编译前就被处理掉,不会出现在 IL 或最终程序集中——这意味着写错 #if 块或漏写 #endif 不会导致运行时报错,但可能让关键逻辑彻底不编译。
为什么 #define 必须放在文件最顶部?
因为 #define 和 #undef 的作用域仅限于当前源文件,且必须在任何可执行代码(包括 using 语句)之前出现。一旦编译器开始解析命名空间或类型声明,就不再接受符号定义。
-
#define不是变量,没有值,只是一个“开关”:它只影响后续的#if判断结果 - 同一文件中多次
#define DEBUG是合法的,但只有第一个生效;#undef DEBUG后再#define DEBUG会重新激活该符号 - 项目级定义(如 MSBuild 中的
<defineconstants>DEBUG;TRACE</defineconstants>>)优先级高于文件内#define,但无法被单个文件内的#undef影响
#if 支持哪些表达式?哪些不支持?
#if 只能判断预定义符号或用 #define 定义的符号是否存在,不支持任意 C# 表达式(比如不能写 #if VERSION == 2 或 #if (DEBUG && !RELEASE) 中的括号不是必须的,但逻辑运算符 &&、||、! 是允许的)。
Miller (mlr) 是一个命令行工具,用于查询、整形和重新格式化名称索引数据,如 CSV、TSV、JSON 和 JSON Lines。它将 awk、sed、cut、join 和 sort 的功能整合到一个专为结构化数据处理而构建的单一工具中。
- 合法:
#if DEBUG || TRACE、#if DEBUG && !BETA、#if NET6_0(框架预定义符号) - 非法:
#if SOME_CONSTANT == 42(这不是符号,是常量值)、#if typeof(string) == typeof(object)(类型检查不可用) - 注意:
#elif后面必须跟新符号或表达式,不能只写#elif(会报错CS1038: #elif expected)
#warning 和 #error 的实际用途
它们不是调试辅助工具,而是编译阶段的“门禁”——尤其适合拦截不兼容的配置或强制开发者决策。
-
#error会让编译直接失败,比如在不支持某 SDK 的旧项目里加:#if !NET7_0 #error This feature requires .NET 7.0 or higher -
#warning用于提醒但不中断构建,例如:#if DEBUG #warning Logging is enabled in DEBUG mode — avoid in production - 二者都支持字符串字面量,但不能拼接变量或调用方法,内容必须是静态文本
- 错误/警告信息会显示在 Visual Studio 的“错误列表”窗口中,并带具体行号,方便定位
#region 和 #endregion 的真实限制
它们只是 IDE 层面的折叠标记,对编译器完全透明,也不影响任何生成行为——但正因为“无害”,很多人误以为可以嵌套使用或跨方法边界放置。
- Visual Studio 和 Rider 支持折叠,但
dotnet build命令行编译完全无视它们 - 不允许嵌套同名
#region(比如两个#region "Helpers"连续出现),否则部分 IDE 会折叠错乱 - 不能出现在方法体内以外的语法错误位置,例如不能在
class声明中间断开(会触发CS1513: } expected类错误) - 真正容易被忽略的是:如果把大量业务逻辑塞进一个
#region,反而掩盖了设计问题——它解决的是视觉杂乱,不是架构混乱









