Директивы препроцессора

Подсказка

Вы новичок в разработке программного обеспечения? Директивы препроцессора не потребуются сразу. Сначала сосредоточьтесь на руководствах по началу работы и вернитесь сюда, когда для проектов требуется условная компиляция или конфигурация сборки.

Есть опыт на другом языке? Если вы знакомы с #ifdef C/C++ или условной компиляцией на других языках, директивы препроцессора C# работают аналогично. Перейдите к нужному синтаксису.

Директивы препроцессора C# сообщают компилятору, какой код следует включать, исключать или обрабатывать по-разному при сборке приложения. Это руководство может изменить итоговую программу. Директивы препроцессора всегда начинаются с # и должны отображаться в собственной строке (игнорируя ведущие пробелы). После директивы можно добавить конечный комментарий. Хотя языковой справочник документирует все доступные директивы, три группы охватывают повседневное использование:

  • Приложения на основе файлов (#:) — настройка файловых приложений.
  • Условная компиляция (#if / #elif / #else / #endif) — включение или исключение кода на основе конфигурации сборки или целевой платформы.
  • Подавление предупреждений (#pragma warning) — подавление или восстановление определенных предупреждений компилятора.

Директивы приложений, основанные на файлах

Начиная с C# 14 , приложения на основе файлов используют две дополнительные директивы:

  • #! — строка shebang , которая позволяет выполнять файл непосредственно в Unix (например, ./Program.cs). Для этого требуется разрешение на выполнение для файла (chmod +x <file>).
  • #: — директивы build-system, которые настраивают пакеты, параметры пакета 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 символ при сборке в конфигурации Debug. Вам не нужно определять его самостоятельно. Символы целевой платформы, такие как NET10_0_OR_GREATER и NET8_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, вместо отключения всех предупреждений. Этот подход сохраняет целевое подавление и дает понять , почему предупреждение подавляется.