Direktivy preprocesoru jazyka C#

I když kompilátor nemá samostatný preprocesor, směrnice popsané v této části jsou zpracovávány, jako kdyby existovala jedna z nich. Můžete je použít k usnadnění podmíněné kompilace. Na rozdíl od direktiv jazyka C a C++ nemůžete pomocí těchto direktiv vytvořit makra. Direktiva preprocesoru musí být jedinou instrukcí na řádku.

Kontext s možnou hodnotou null

#nullableDirektiva preprocesoru nastavuje kontext anotace s možnou hodnotou null a kontext s možnou hodnotou null. Tato direktiva řídí, zda mají poznámky s možnou hodnotou null a zda jsou zadány výstrahy s hodnotou null. Každý kontext je buď zakázán , nebo povolen.

Oba kontexty lze zadat na úrovni projektu (mimo zdrojový kód jazyka C#). #nullableDirektiva řídí kontexty poznámek a upozornění a má přednost před nastaveními na úrovni projektu. Direktiva nastavuje kontexty, které ovládací prvky řídí, dokud ji nepřepisuje jiná direktiva nebo dokud nekončí konec zdrojového souboru.

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

  • #nullable disable: Nastavuje anotaci a kontexty s možnou hodnotou null na disabled.
  • #nullable enable: Nastaví pro povolenou anotaci a kontexty upozornění s možnou hodnotou null.
  • #nullable restore: Obnoví nastavení projektu s možnou anotací a kontexty upozornění.
  • #nullable disable annotations: Nastaví kontext anotace s možnou hodnotou null na disabled.
  • #nullable enable annotations: Nastaví kontext anotace s možnou hodnotou null na povoleno.
  • #nullable restore annotations: Obnoví kontext anotace s možnou hodnotou null na nastavení projektu.
  • #nullable disable warnings: Nastaví výstražný kontext s možnou hodnotou null na disabled.
  • #nullable enable warnings: Nastaví výstražný kontext s možnou hodnotou null na povoleno.
  • #nullable restore warnings: Obnoví kontext upozornění s možnou hodnotou null na nastavení projektu.

Podmíněná kompilace

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

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

Pokud kompilátor jazyka C# najde #if direktivu, za kterou následuje #endif směrnice, 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 přiřadit číselnou hodnotu k symbolu. #ifPříkaz v jazyce C# je logický a testuje pouze, zda byl symbol definován nebo nikoli. Například:

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

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

#ifspolečně s #else #elif direktivami,, #endif , #define a #undef 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 kompilování kódu pro sestavení pro ladění nebo při kompilování pro konkrétní konfiguraci.

Podmíněná direktiva začínající #if direktivou musí být explicitně ukončena #endif direktivou. #define umožňuje definovat symbol. Když použijete symbol jako výraz předaný #if direktivě, výraz se vyhodnotí jako true . Můžete také definovat symbol pomocí možnosti kompilátoru DefineConstants . Symbol můžete zrušit definováním pomocí #undef . Rozsah symbolu vytvořeného pomocí #define je soubor, ve kterém byl definován. Symbol, který definujete pomocí DefineConstants nebo s, #define není v konfliktu 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 může být vyhodnocen pouze direktivou preprocesoru.

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

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

#else umožňuje vytvořit složenou podmíněnou direktivu, takže pokud žádný z výrazů v předchozí #if nebo (volitelné) #elif direktivy není vyhodnocen jako true , kompilátor vyhodnotí všechen kód mezi #else a další #endif . #endif(#endif) musí být následující direktiva preprocesoru po #else .

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

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

Cílové architektury Symboly
.NET Framework NETFRAMEWORK, NET48, NET472, NET471, NET47, NET462, NET461, NET46, NET452, NET451, NET45, NET40, NET35, NET20
.NET Standard NETSTANDARD, NETSTANDARD2_1, NETSTANDARD2_0, NETSTANDARD1_6, NETSTANDARD1_5, NETSTANDARD1_4, NETSTANDARD1_3, NETSTANDARD1_2, NETSTANDARD1_1, NETSTANDARD1_0
.NET 5 + (a .NET Core) NET, NET6_0, NET6_0_ANDROID, NET6_0_IOS, NET6_0_MACOS, NET6_0_MACCATALYST, NET6_0_TVOS, NET6_0_WINDOWS, NET5_0, NETCOREAPP, NETCOREAPP3_1, NETCOREAPP3_0, NETCOREAPP2_2, NETCOREAPP2_1, NETCOREAPP2_0, NETCOREAPP1_1, NETCOREAPP1_0

Poznámka

Pro tradiční projekty, které nejsou v sadě SDK, je nutné 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 TRACE konstanty a. Můžete přepsat hodnoty nastavené pro projekt pomocí #define . Symbol ladění, například, je automaticky nastaven v závislosti na vlastnostech konfigurace sestavení ("ladění" nebo "Release" Mode).

Následující příklad ukazuje, jak definovat MYTEST symbol v souboru a potom otestovat hodnoty MYTEST DEBUG symbolů a. Výstup tohoto příkladu závisí na tom, zda jste projekt sestavili v režimu konfigurace ladění nebo vydání .

#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á rozhraní, 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ů

Použijte následující dvě direktivy preprocesoru k definování nebo zrušení definování symbolů pro podmíněnou kompilaci:

  • #define: Definujte symbol.
  • #undef: Zrušit definici symbolu.

Použijte #define k definování symbolu. Použijete-li symbol jako výraz, který je předán #if direktivě, bude výraz vyhodnocen true jako, jak ukazuje následující příklad:

#define VERBOSE

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

Poznámka

#defineDirektiva nemůže být použita k deklaraci konstantních hodnot, jak je obvykle prováděna v jazyce C a C++. Konstanty v jazyce C# jsou nejlépe definovány jako statické členy třídy nebo struktury. Pokud máte několik takových konstant, zvažte vytvoření samostatné třídy "konstanty" pro jejich uložení.

Symboly lze použít k zadání podmínek pro kompilaci. Můžete testovat symbol pomocí #if nebo #elif . Můžete také použít ConditionalAttribute k provedení podmíněné kompilace. Můžete definovat symbol, ale nemůžete přiřadit hodnotu k symbolu. #defineDirektiva musí být uvedena v souboru předtím, než použijete pokyny, které nejsou také direktivami preprocesoru. Můžete také definovat symbol pomocí možnosti kompilátoru DefineConstants . Symbol můžete zrušit definováním pomocí #undef .

Definování oblastí

Můžete definovat oblasti kódu, které mohou být sbaleny do osnovy pomocí následujících dvou direktiv preprocesoru:

  • #region: Začátek oblasti.
  • #endregion: Konec oblasti

#region umožňuje zadat blok kódu, který lze rozbalit nebo sbalit při použití funkce sbalení editoru kódu. V případě delších souborů 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

#regionBlok musí být ukončen #endregion direktivou. #regionBlok se nemůže překrývat s #if blokem. #regionBlok však může být vnořen v #if bloku a #if blok může být vnořen do #region bloku.

Informace o chybě a upozornění

Instruujte kompilátor, aby generoval uživatelsky definované chyby kompilátoru a upozornění a informace o ovládacích řádcích pomocí následujících direktiv:

  • #error: Vygeneruje chybu kompilátoru se zadanou zprávou.
  • #warning: Vygeneruje upozornění kompilátoru s určitou zprávou.
  • #line: Změňte číslo řádku vytištěného pomocí zpráv kompilátoru.

#error umožňuje generovat CS1029 uživatelem definované chyby z konkrétního umístění v kódu. Například:

#error Deprecated code in this method.

Poznámka

Kompilátor zpracovává #error version speciální způsob a hlásí chybu kompilátoru CS8304 se zprávou obsahující použitou kompilátor a jazykové verze.

#warning umožňuje generovat upozornění na CS1030 úrovně jednoho kompilátoru z konkrétního umístění v kódu. Napří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 ohlásit dvě upozornění spojená s čísly řádků. #line 200Direktiva vynutí, aby číslo dalšího řádku bylo 200 (výchozí hodnota je #6), a až do další #line direktivy bude název souboru uveden jako "Special". #line defaultDirektiva vrátí číslování řádků do výchozího číslování, což spočí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 generuje 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

#lineDirektiva může být použita v automatizovaném mezilehlé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í řádků v souboru, mohli byste odebrat řádky a potom simulovat původní číslování řádků #line .

#line hiddenDirektiva skrývá po sobě jdoucí řádky z ladicího programu, takže když kroky pro vývojáře prostřednictvím kódu, všechny řádky mezi #line hidden a a další #line direktivou (za předpokladu, že se nejedná o jinou #line hidden direktivu), se zvýší. Tato možnost slouží také k tomu, aby ASP.NET bylo možné odlišit od uživatelsky definovaného a strojově generovaného kódu. I když je ASP.NET primárním spotřebitelem této funkce, je pravděpodobnější, že ji budou používat více generátorů zdrojů.

#line hiddenDirektiva nemá vliv na názvy souborů nebo čísla řádků při zasílání zpráv o chybách. 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.

#line filenameDirektiva Určuje název souboru, který se má zobrazit ve výstupu kompilátoru. Ve výchozím nastavení se používá skutečný název souboru se zdrojovým kódem. Název souboru musí být v uvozovkách ("") a musí předcházet číslo řádku.

Následující příklad ukazuje, jak ladicí program ignoruje skryté řádky v kódu. Když spustíte příklad, zobrazí se tři řádky textu. Nicméně pokud nastavíte bod přerušení, jak je znázorněno v příkladu, a stisknutím klávesy F10 provedete krok kódu, ladicí program ignoruje skrytý řádek. I v případě, že jste nastavili bod přerušení na skrytém řádku, ladicí program ho bude stále ignorovat.

// preprocessor_linehidden.cs
using System;
class MainClass
{
    static void Main()
    {
        Console.WriteLine("Normal line #1."); // Set break point here.
#line hidden
        Console.WriteLine("Hidden line.");
#line default
        Console.WriteLine("Normal line #2.");
    }
}

Pragmas

#pragma poskytne 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 vytvoření vlastních instrukcí pro předzpracování.

#pragma pragma-name pragma-arguments

Kde pragma-name je název rozpoznané direktivy pragma a pragma-arguments je argumentem specifickým 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 čárkami oddělený seznam čísel upozornění. Předpona CS je volitelná. Pokud nejsou zadaná žádná čísla upozornění, disable zakáže všechna upozornění a restore povolí všechna upozornění.

Poznámka

Chcete-li najít čísla upozornění v aplikaci Visual Studio, sestavte projekt a vyhledejte čísla upozornění v okně výstup .

disableZačne platit na dalším řádku zdrojového souboru. Upozornění se obnoví na řádku, který následuje po restore . Pokud restore v souboru není, upozornění se obnoví na výchozí stav 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

Vygeneruje kontrolní součty pro zdrojové soubory, které pomáhají s laděním ASP.NET stránek.

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

Kde "filename" je název souboru, který vyžaduje sledování změn nebo aktualizací, "{guid}" je globálně jedinečný identifikátor (GUID) pro algoritmus hash a "checksum_bytes" je řetězec hexadecimálních číslic, které představují bajty kontrolního součtu. Musí se jednat o sudý počet šestnáctkových číslic. Lichý počet číslic má za následek upozornění při kompilaci a direktiva je ignorována.

Ladicí program sady Visual Studio používá kontrolní součet k tomu, aby se ujistil, že vždy najde správný zdroj. Kompilátor vypočítá kontrolní součet pro zdrojový soubor a poté vygeneruje výstup do souboru programu databáze (PDB). Ladicí program pak pomocí PDB porovná s kontrolním součtem, který počítá pro zdrojový soubor.

Toto řešení nefunguje pro projekty ASP.NET, protože vypočtený kontrolní součet je pro generovaný zdrojový soubor, nikoli soubor. aspx. Pro vyřešení tohoto problému #pragma checksum poskytuje podporu kontrolního součtu pro stránky ASP.NET.

Při vytváření projektu ASP.NET v jazyce Visual C# vygenerovaný zdrojový soubor obsahuje kontrolní součet pro soubor. aspx, ze kterého je zdroj generován. Kompilátor potom tyto informace zapíše do souboru PDB.

Pokud kompilátor nenajde #pragma checksum v souboru žádnou direktivu, 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
    }
}