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 | Další symboly dostupné v sadě .NET SDK 5 nebo více |
|---|---|---|
| .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, 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 |
NET6_0_OR_GREATER, NET6_0_ANDROID_OR_GREATER, NET6_0_IOS_OR_GREATER, NET6_0_MACOS_OR_GREATER, NET6_0_MACCATALYST_OR_GREATER, NET6_0_TVOS_OR_GREATER, NET6_0_WINDOWS_OR_GREATER, NET5_0_OR_GREATER, NETCOREAPP_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 definované jenom pro verzi, na kterou cílíte.
- Symboly
<framework>_OR_GREATERjsou definované pro verzi, na kterou cílíte, a všechny starší verze. Pokud například cílíte na .NET Framework 2.0, definují se následující symboly:NET_2_0,NET_2_0_OR_GREATER, aNET_1_1_OR_GREATERNET_1_0_OR_GREATER.
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 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 mohl rozlišovat mezi uživatelsky definovaným a strojově generovaným kódem. i když je ASP.NET primárním spotřebitelem této funkce, je pravděpodobnější, že ji budou využí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.
Počínaje jazykem C# 10 můžete použít novou formu této #line direktivy:
#line (1, 1) - (5, 60) 10 "partial-class.g.cs"
/*34567*/int b = 0;
Komponenty tohoto formuláře jsou:
(1, 1): Počáteční řádek a sloupec prvního znaku na řádku, který následuje po direktivě. V tomto příkladu by se další řádek nahlásil jako řádek 1, sloupec 1.(5, 60): Koncový řádek a sloupec pro označenou oblast.10: Posun sloupce pro#linedirektivu, která se má projevit. V tomto příkladu bude desátý sloupec hlášen jako sloupec One. To je místo, kde je deklaraceint b = 0;zahájena. Toto pole je volitelné. Pokud tento parametr vynecháte, direktiva se projeví v prvním sloupci."partial-class.g.cs": Název výstupního souboru.
Předchozí příklad vygeneruje 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 přemapování b je proměnná na prvním řádku, ve znaku šest.
Jazyky specifické pro doménu (DSL) obvykle používají tento formát k poskytnutí lepšího mapování ze zdrojového souboru do vygenerovaného výstupu v jazyce C#. Chcete-li zobrazit další příklady tohoto formátu, přečtěte si část specifikace funkce v části příklady.
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 warning: Povolí nebo zakáže upozornění.#pragma checksum: Vygenerujte kontrolní součet.
#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 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 pro podporu ladění 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 Visual Studio používá kontrolní součet k tomu, aby bylo zajištěno, ž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 ASP.NET projekty, protože vypočtený kontrolní součet je pro generovaný zdrojový soubor, nikoli soubor. aspx. chcete-li vyřešit tento problém, #pragma checksum poskytuje podporu kontrolního součtu pro ASP.NET stránky.
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
}
}