预处理器指令

小窍门

开发软件的新手? 无需立即使用预处理器指令。 请首先关注 入门 教程,并在项目需要条件编译或生成配置时返回此处。

是否在其他语言中有经验? 如果你熟悉 #ifdef C/C++ 或其他语言的条件编译,C# 预处理器指令的工作方式类似。 提前了解所需的语法。

C# 预处理器指令告诉编译器在生成应用时要包含、排除或处理哪些代码不同。 本指南可以更改生成的程序。 预处理器指令始终以 # 开头,并且必须出现在自己的行上(忽略前导空格)。 可以在指令后添加尾随注释。 尽管语言参考文档记录了所有可用的指令,但其中有三个组涵盖了日常使用:

  • 基于文件的应用#:) - 配置基于文件的应用。
  • 条件编译#if / #elif / #else / #endif) - 基于生成配置或目标框架包含或排除代码。
  • 警告抑制#pragma warning) - 禁止或还原特定的编译器警告。

基于文件的应用指令

从 C# 14 开始, 基于文件的应用 使用两个附加指令:

  • #!— 支持直接在 Unix 上执行文件的 shebang 行(例如)。 ./Program.cs 这要求对文件 () 设置chmod +x <file>权限。
  • #: — 用于为单文件程序配置包、SDK 设置和其他选项的生成系统指令。

使用 #:package 添加 NuGet 包。 例如,以下基于文件的应用使用 Spectre.Console 包来呈现样式的输出:

#!/usr/bin/env dotnet
#:package Spectre.Console@*

AnsiConsole.MarkupLine("[bold green]Hello[/] from a file-based app!");

可以通过 @ 指定确切的版本,或者通过 @* 拉取最新版本。 添加多个 #:package 指令以包含更多包:

#:package Serilog@3.1.1

使用其他 #: 指令可以引用项目、设置 MSBuild 属性或更改 SDK:

#:project ../SharedLibrary/SharedLibrary.csproj
#:property PublishAot=false
#:sdk Microsoft.NET.Sdk.Web

有关指令的完整列表,请参阅 基于文件的应用语言参考

条件编译

根据是否定义符号,使用#if#elif#else#endif包括或排除代码。 最常见的符号是 DEBUG (自动为调试生成设置)和目标框架符号,例如 NET10_0_OR_GREATER

static void ConfigureLogging()
{
#if DEBUG
    Console.WriteLine("Debug logging enabled — verbose output active.");
#else
    Console.WriteLine("Release logging — errors only.");
#endif
}

生成系统在调试配置中生成时定义 DEBUG 符号。 你不需要自己定义它。 目标框架符号,例如 NET10_0_OR_GREATERNET8_0_OR_GREATER,让您可以编写能够适应多目标项目中不同 .NET 版本的代码。

可以将符号与逻辑运算符组合在一起: && (和)、 || (或)和 ! (而不是):

static void ShowPlatformInfo()
{
#if NET10_0_OR_GREATER
    Console.WriteLine("Running on .NET 10 or later.");
#elif NET8_0_OR_GREATER
    Console.WriteLine("Running on .NET 8 or 9.");
#else
    Console.WriteLine("Running on an older .NET version.");
#endif
}

使用 #define 文件顶部定义自己的符号。 还可以使用 DefineConstants 项目文件中的属性定义整个项目的符号。

警告抑制功能

用于 #pragma warning disable 禁止显示特定的编译器警告,并 #pragma warning restore 重新启用它们。 始终尽可能缩小抑制范围:

    static void ProcessData()
    {
        try
        {
            // Some operation that might fail
            var data = File.ReadAllText("config.json");
            Console.WriteLine($"Config loaded: {data.Length} characters");
        }
#pragma warning disable CS0168 // Variable is declared but never used
        catch (FileNotFoundException ex)
#pragma warning restore CS0168
        {
            // Fall back to defaults — the exception details aren't needed here
            Console.WriteLine("Config file not found, using defaults.");
        }
    }

小窍门

始终指定警告号,例如 CS0168,而不是禁用所有警告。 此方法将保持抑制目标,并明确 为什么 禁止显示警告。