Direktivy preprocesoru jazyka C#

I když kompilátor nemá samostatný preprocesor, direktivy popsané v této části se zpracovávají, jako by existovaly. Používáte je k tomu, abyste pomohli s podmíněnou kompilací. Na rozdíl od direktiv jazyka C a C++ nemůžete tyto direktivy použít k vytváření maker. Direktiva preprocesoru musí být jedinou instrukcí na řádku.

Kontext s možnou hodnotou null

Direktiva #nullable preprocesoru nastaví kontext poznámky s možnou hodnotou null a kontext upozornění s možnou hodnotou null. Tato direktiva určuje, jestli mají poznámky s možnou hodnotou null vliv a zda jsou uvedena upozornění na nulovost. Každý kontext je buď zakázaný , nebo povolený.

Oba kontexty je možné zadat na úrovni projektu (mimo zdrojový kód jazyka C#). Direktiva #nullable řídí kontexty poznámek a upozornění a má přednost před nastavením na úrovni projektu. Direktiva nastaví kontexty, které řídí, dokud ji nepřepíše jiná direktiva, nebo až do konce zdrojového souboru.

Účinek direktiv je následující:

  • #nullable disable: Nastaví kontexty s možnou hodnotou null a upozornění na zakázané.
  • #nullable enable: Nastaví kontexty s možnou hodnotou null a upozornění na povolenou.
  • #nullable restore: Obnoví kontexty poznámek s možnou hodnotou null a upozornění do nastavení projektu.
  • #nullable disable annotations: Nastaví kontext poznámek s možnou hodnotou null na zakázáno.
  • #nullable enable annotations: Nastaví kontext poznámek s možnou hodnotou null na povolenou.
  • #nullable restore annotations: Obnoví kontext poznámky s možnou hodnotou null do nastavení projektu.
  • #nullable disable warnings: Nastaví kontext upozornění s možnou hodnotou null na zakázání.
  • #nullable enable warnings: Nastaví kontext upozornění s možnou hodnotou null na povolenou.
  • #nullable restore warnings: Obnoví kontext upozornění s možnou hodnotou null do nastavení projektu.

Podmíněná kompilace

K řízení podmíněné kompilace použijete čtyři direktivy preprocesoru:

  • #if: Otevře podmíněnou kompilaci, kde je kód zkompilován pouze v případě, že je definovaný zadaný symbol.
  • #elif: Zavře předchozí podmíněnou kompilaci a otevře novou podmíněnou kompilaci na základě toho, jestli je definovaný zadaný symbol.
  • #else: Zavře předchozí podmíněnou kompilaci a otevře novou podmíněnou kompilaci, pokud předchozí zadaný symbol není definován.
  • #endif: Zavře předchozí podmíněnou kompilaci.

Když kompilátor jazyka C# najde direktivu #if , po které nakonec #endif následuje direktiva, zkompiluje kód mezi direktivami pouze v případě, že je definován zadaný symbol. Na rozdíl od jazyka C a C++ nemůžete k symbolu přiřadit číselnou hodnotu. Příkaz #if v jazyce C# je logická hodnota a testuje pouze to, jestli byl symbol definován, nebo ne. Příklad:

#if DEBUG
    Console.WriteLine("Debug version");
#endif

Operátory == (rovnost) a != (nerovnost) můžete použít k otestování bool hodnot true nebo false. true znamená, že je definován symbol. Příkaz #if DEBUG má stejný význam jako #if (DEBUG == true). Operátory && (a)|| (nebo) a ! (ne) můžete použít k vyhodnocení, jestli bylo definováno více symbolů. Symboly a operátory je také možné seskupovat pomocí závorek.

#ifspolu s direktivami #else, , , #undef#elif#endif#definea umožňuje zahrnout nebo vyloučit kód na základě existence jednoho nebo více symbolů. Podmíněná kompilace může být užitečná při kompilaci kódu pro sestavení ladění nebo při kompilaci pro konkrétní konfiguraci.

Podmíněná direktiva začínající direktivou #if musí být výslovně ukončena direktivou #endif . #define umožňuje definovat symbol. Použitím symbolu jako výrazu předaného #if direktivě se výraz vyhodnotí jako true. Symbol můžete také definovat pomocí možnosti DefineConstants kompilátoru. Symbol můžete nedefinovat znakem #undef. Obor symbolu vytvořeného pomocí #define je soubor, ve kterém byl definován. Symbol, který definujete pomocí DefineConstants nebo s #define nekomlikuje s proměnnou stejného názvu. To znamená, že název proměnné by neměl být předán direktivě preprocesoru a symbol lze vyhodnotit pouze direktivou preprocesoru.

Výraz #elif umožňuje vytvořit složenou podmíněnou direktivu. Výraz #elif se vyhodnotí, pokud se nevyhodnotí truepředchozí #if ani předchozí, volitelné #elif výrazy direktivy . Pokud se #elif výraz vyhodnotí jako true, kompilátor vyhodnotí veškerý kód mezi #elif a další podmíněnou direktivou. Příklad:

#define VC7
//...
#if debug
    Console.WriteLine("Debug build");
#elif VC7
    Console.WriteLine("Visual Studio 7");
#endif

#elseumožňuje vytvořit složenou podmíněnou direktivu, aby kompilátor vyhodnotil trueveškerý kód mezi #else a dalšími #endifvýrazy v předchozích #if nebo (volitelných) #elif direktivách. #endif(#endif) musí být další direktivou preprocesoru za #else.

#endif určuje konec podmíněné direktivy, která začala direktivou #if .

Systém sestavení také znát předdefinované symboly preprocesoru představující různé cílové architektury v projektech ve stylu sady SDK. Jsou užitečné při vytváření aplikací, které můžou cílit na více než jednu verzi .NET.

Cílové architektury Symboly Další symboly dostupné v sadě .NET 5+ SDK
.NET Framework NETFRAMEWORK, NET48, NET472, NET471, NET47, NET462, NET461, NET46, NET452, NET451, NET45, NET40, NET35, NET20 NET48_OR_GREATER, NET472_OR_GREATER, NET471_OR_GREATER, NET47_OR_GREATER, NET462_OR_GREATER, NET461_OR_GREATER, NET46_OR_GREATER, NET452_OR_GREATER, NET451_OR_GREATER, NET45_OR_GREATER, NET40_OR_GREATER, NET35_OR_GREATER, NET20_OR_GREATER
.NET Standard NETSTANDARD, NETSTANDARD2_1, NETSTANDARD2_0, NETSTANDARD1_6, NETSTANDARD1_5, NETSTANDARD1_4, NETSTANDARD1_3, NETSTANDARD1_2, NETSTANDARD1_1, NETSTANDARD1_0 NETSTANDARD2_1_OR_GREATER, NETSTANDARD2_0_OR_GREATER, NETSTANDARD1_6_OR_GREATER, NETSTANDARD1_5_OR_GREATER, NETSTANDARD1_4_OR_GREATER, NETSTANDARD1_3_OR_GREATER, NETSTANDARD1_2_OR_GREATER, NETSTANDARD1_1_OR_GREATER, NETSTANDARD1_0_OR_GREATER
.NET 5+ (a .NET Core) NET, NET7_0, NET6_0, NET5_0, NETCOREAPP, NETCOREAPP3_1, NETCOREAPP3_0, NETCOREAPP2_2, NETCOREAPP2_1, NETCOREAPP2_0, NETCOREAPP1_1, NETCOREAPP1_0 NET7_0_OR_GREATER, NET6_0_OR_GREATER, NET5_0_OR_GREATER, NETCOREAPP3_1_OR_GREATER, NETCOREAPP3_0_OR_GREATER, NETCOREAPP2_2_OR_GREATER, NETCOREAPP2_1_OR_GREATER, NETCOREAPP2_0_OR_GREATER, NETCOREAPP1_1_OR_GREATER, NETCOREAPP1_0_OR_GREATER

Poznámka

  • Symboly bez verzí se definují bez ohledu na verzi, na kterou cílíte.
  • Symboly specifické pro verzi jsou definovány pouze pro verzi, na kterou cílíte.
  • Symboly <framework>_OR_GREATER jsou definované pro verzi, na kterou cílíte, a všechny starší verze. Pokud například cílíte na rozhraní .NET Framework 2.0, jsou definovány následující symboly: NET20, , NET20_OR_GREATERNET11_OR_GREATERa NET10_OR_GREATER.
  • Liší se od monikers cílové architektury (TFMs) používané vlastností MSBuild TargetFramework a NuGet.

Poznámka

U tradičních projektů bez sady SDK musíte ručně nakonfigurovat symboly podmíněné kompilace pro různé cílové architektury v sadě Visual Studio prostřednictvím stránek vlastností projektu.

Mezi další předdefinované symboly patří DEBUG konstanty a TRACE konstanty. Hodnoty nastavené pro projekt můžete přepsat pomocí #define. Například symbol LADĚNÍ se automaticky nastaví v závislosti na vlastnostech konfigurace sestavení (režim Ladění nebo Release).

Následující příklad ukazuje, jak definovat MYTEST symbol v souboru a pak otestovat hodnoty MYTEST a DEBUG symboly. Výstup tohoto příkladu závisí na tom, jestli jste projekt vytvořili v režimu konfigurace ladění nebo verze .

#define MYTEST
using System;
public class MyClass
{
    static void Main()
    {
#if (DEBUG && !MYTEST)
        Console.WriteLine("DEBUG is defined");
#elif (!DEBUG && MYTEST)
        Console.WriteLine("MYTEST is defined");
#elif (DEBUG && MYTEST)
        Console.WriteLine("DEBUG and MYTEST are defined");  
#else
        Console.WriteLine("DEBUG and MYTEST are not defined");
#endif
    }
}

Následující příklad ukazuje, jak otestovat pro různé cílové architektury, abyste mohli používat novější rozhraní API, pokud je to možné:

public class MyClass
{
    static void Main()
    {
#if NET40
        WebClient _client = new WebClient();
#else
        HttpClient _client = new HttpClient();
#endif
    }
    //...
}

Definování symbolů

Pomocí následujících dvou direktiv preprocesoru definujete nebo nedefinujete symboly pro podmíněnou kompilaci:

  • #define: Definujte symbol.
  • #undef: Nedefinovat symbol.

Slouží #define k definování symbolu. Když použijete symbol jako výraz předaný direktivě #if , výraz se vyhodnotí jako true, jak ukazuje následující příklad:

#define VERBOSE

#if VERBOSE
   Console.WriteLine("Verbose output version");
#endif

Poznámka

Direktivu #define nelze použít k deklaraci konstantních hodnot, jak je obvykle provedeno v jazyce C a C++. Konstanty v jazyce C# jsou nejlépe definované jako statické členy třídy nebo struktury. Pokud máte několik takových konstant, zvažte vytvoření samostatné třídy "Konstanty", která je bude obsahovat.

Symboly lze použít k určení podmínek kompilace. Symbol můžete otestovat pomocí symbolu #if nebo #elif. Můžete také použít ConditionalAttribute k provedení podmíněné kompilace. Symbol můžete definovat, ale nemůžete přiřadit hodnotu symbolu. Před #define použitím jakýchkoli pokynů, které nejsou direktivami preprocesoru, se musí direktiva objevit v souboru. Symbol můžete také definovat pomocí možnosti DefineConstants kompilátoru. Symbol můžete nedefinovat znakem #undef.

Definování oblastí

Oblasti kódu, které je možné sbalit v osnově, můžete definovat pomocí následujících dvou direktiv preprocesoru:

  • #region: Spusťte oblast.
  • #endregion: Ukončení oblasti.

#region umožňuje zadat blok kódu, který můžete rozbalit nebo sbalit při použití funkce osnovy editoru kódu. V delších souborech kódu je vhodné sbalit nebo skrýt jednu nebo více oblastí, abyste se mohli soustředit na část souboru, na kterém právě pracujete. Následující příklad ukazuje, jak definovat oblast:

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

Blok #region musí být ukončen direktivou #endregion . Blok #region se nemůže překrývat s blokem #if . #region Blok se ale dá vnořit do #if bloku a #if blok se dá vnořit do #region bloku.

Informace o chybách a upozorněních

Kompilátoru dáváte pokyn, aby vygeneroval chyby a upozornění kompilátoru definované uživatelem a informace o řídicím řádku pomocí následujících direktiv:

  • #error: Vygenerování chyby kompilátoru se zadanou zprávou
  • #warning: Vygenerujte upozornění kompilátoru s konkrétní zprávou.
  • #line: Změňte číslo řádku vytištěné se zprávami kompilátoru.

#error umožňuje vygenerovat uživatelem definovanou chybu CS1029 z konkrétního umístění v kódu. Příklad:

#error Deprecated code in this method.

Poznámka

Kompilátor zachází #error version speciálním způsobem a hlásí chybu kompilátoru CS8304 se zprávou obsahující použitý kompilátor a jazykové verze.

#warning umožňuje vygenerovat upozornění kompilátoru CS1030 na úrovni 1 z konkrétního umístění v kódu. Příklad:

#warning Deprecated code in this method.

#line umožňuje upravit číslování řádků kompilátoru a (volitelně) výstup názvu souboru pro chyby a upozornění.

Následující příklad ukazuje, jak nahlásit dvě upozornění přidružená k číselám řádků. Direktiva #line 200 vynutí číslo dalšího řádku na hodnotu 200 (i když výchozí hodnota je #6) a do další #line direktivy bude název souboru hlášen jako "Special". Direktiva #line default vrátí číslování řádků na výchozí číslování, které počítá řádky, které byly přečíslovány předchozí direktivou.

class MainClass
{
    static void Main()
    {
#line 200 "Special"
        int i;
        int j;
#line default
        char c;
        float f;
#line hidden // numbering not affected
        string s;
        double d;
    }
}

Kompilace vytvoří následující výstup:

Special(200,13): warning CS0168: The variable 'i' is declared but never used
Special(201,13): warning CS0168: The variable 'j' is declared but never used
MainClass.cs(9,14): warning CS0168: The variable 'c' is declared but never used
MainClass.cs(10,15): warning CS0168: The variable 'f' is declared but never used
MainClass.cs(12,16): warning CS0168: The variable 's' is declared but never used
MainClass.cs(13,16): warning CS0168: The variable 'd' is declared but never used

Direktiva #line se může použít v automatizovaném přechodném kroku procesu sestavení. Například pokud byly řádky odebrány z původního souboru zdrojového kódu, ale přesto jste chtěli, aby kompilátor vygeneroval výstup na základě původního číslování řádku v souboru, můžete odebrat řádky a pak simulovat původní číslování řádku s #line.

Direktiva #line hidden skryje následující řádky od ladicího programu, takže když vývojář provede kód, všechny řádky mezi #line hidden a další #line direktivou (za předpokladu, že to není jiná #line hidden direktiva), budou stupňovité. Tato možnost se dá použít také k tomu, aby ASP.NET rozlišovala mezi uživatelem definovaným a strojově vygenerovaným kódem. I když ASP.NET je primárním příjemcem této funkce, je pravděpodobné, že ho bude využívat více zdrojových generátorů.

Direktiva #line hidden nemá vliv na názvy souborů ani čísla řádků v hlášení chyb. To znamená, že pokud kompilátor najde chybu ve skrytém bloku, kompilátor oznámí aktuální název souboru a číslo řádku chyby.

Direktiva #line filename určuje název souboru, který chcete zobrazit ve výstupu kompilátoru. Ve výchozím nastavení se použije skutečný název souboru zdrojového kódu. Název souboru musí být v uvozovkách ("") a musí předcházet číslem řádku.

Počínaje jazykem C# 10 můžete použít novou formu #line direktivy:

#line (1, 1) - (5, 60) 10 "partial-class.g.cs"
/*34567*/int b = 0;

Součásti tohoto formuláře jsou:

  • (1, 1): Počáteční řádek a sloupec prvního znaku na řádku, který následuje za direktivou. V tomto příkladu bude další řádek hlášen jako řádek 1, sloupec 1.
  • (5, 60): Koncový řádek a sloupec pro označenou oblast.
  • 10: Posun sloupce pro direktivu #line se projeví. V tomto příkladu by byl 10. sloupec hlášen jako sloupec jeden. To je místo, kde deklarace int b = 0; začíná. Toto pole je volitelné. Pokud je tato direktiva vynechána, projeví se na prvním sloupci.
  • "partial-class.g.cs": Název výstupního souboru.

Předchozí příklad by vygeneroval následující upozornění:

partial-class.g.cs(1,5,1,6): warning CS0219: The variable 'b' is assigned but its value is never used

Po opětovném namapování je proměnná bna prvním řádku, na znaku šest.

Jazyky specifické pro doménu (DSLS) obvykle používají tento formát k zajištění lepšího mapování ze zdrojového souboru na vygenerovaný výstup jazyka C#. Pokud chcete zobrazit další příklady tohoto formátu, podívejte se na specifikaci funkcí v části s příklady.

Pragmas

#pragma poskytuje kompilátoru zvláštní pokyny pro kompilaci souboru, ve kterém se zobrazí. Pokyny musí kompilátor podporovat. Jinými slovy, nemůžete použít #pragma k vytváření vlastních pokynů k předběžnému zpracování.

#pragma pragma-name pragma-arguments

Kde pragma-name je název rozpoznaného pragma a pragma-arguments je argumenty specifické pro direktivu pragma.

upozornění #pragma

#pragma warning může povolit nebo zakázat určitá upozornění.

#pragma warning disable warning-list
#pragma warning restore warning-list

Kde warning-list je seznam čísel upozornění oddělený čárkami. Předpona CS je volitelná. Pokud nejsou zadána žádná čísla upozornění, disable zakáže všechna upozornění a restore povolí všechna upozornění.

Poznámka

Pokud chcete najít čísla upozornění v sadě Visual Studio, sestavte projekt a vyhledejte čísla upozornění v okně Výstup .

Projeví se disable na dalším řádku zdrojového souboru. Upozornění se obnoví na řádku za restore. Pokud soubor neobsahuje, restore upozornění se obnoví do výchozího stavu na prvním řádku všech pozdějších souborů ve stejné kompilaci.

// pragma_warning.cs
using System;

#pragma warning disable 414, CS3021
[CLSCompliant(false)]
public class C
{
    int i = 1;
    static void Main()
    {
    }
}
#pragma warning restore CS3021
[CLSCompliant(false)]  // CS3021
public class D
{
    int i = 1;
    public static void F()
    {
    }
}

kontrolní součet #pragma

Generuje kontrolní součty pro zdrojové soubory, které vám pomůžou s laděním ASP.NET stránek.

#pragma checksum "filename" "{guid}" "checksum bytes"

Kde "filename" je název souboru, který vyžaduje monitorování změn nebo aktualizací, "{guid}" je globálně jedinečný identifikátor (GUID) pro algoritmus hash a "checksum_bytes" je řetězec šestnáctkových číslic představujících bajty kontrolního součtu. Musí být sudý počet šestnáctkových číslic. Lichý počet číslic vede k upozornění na dobu kompilace a direktiva se ignoruje.

Ladicí program sady Visual Studio používá kontrolní součet, který zajistí, že vždy najde správný zdroj. Kompilátor vypočítá kontrolní součet zdrojového souboru a pak vygeneruje výstup do souboru PDB (Program Database). Ladicí program pak použije pdB k porovnání s kontrolním součtem, který vypočítá pro zdrojový soubor.

Toto řešení nefunguje pro projekty ASP.NET, protože vypočítaný kontrolní součet je pro vygenerovaný zdrojový soubor, nikoli pro .aspx soubor. Pokud chcete tento problém vyřešit, #pragma checksum poskytuje podporu kontrolního součtu pro ASP.NET stránky.

Když vytvoříte projekt ASP.NET v jazyce Visual C#, vygenerovaný zdrojový soubor obsahuje kontrolní součet pro .aspx soubor, ze kterého se zdroj vygeneruje. Kompilátor pak tyto informace zapíše do souboru PDB.

Pokud kompilátor v souboru nenajde direktivu #pragma checksum , vypočítá kontrolní součet a zapíše hodnotu do souboru PDB.

class TestClass
{
    static int Main()
    {
        #pragma checksum "file.cs" "{406EA660-64CF-4C82-B6F0-42D48172A799}" "ab007f1d23d9" // New checksum
    }
}