Delen via


C#-preprocessorrichtlijnen

Hoewel de compiler geen afzonderlijke preprocessor heeft, worden de instructies die in deze sectie worden beschreven, verwerkt alsof er een is. U gebruikt ze om u te helpen bij voorwaardelijke compilatie. In tegenstelling tot C- en C++-instructies kunt u deze instructies niet gebruiken om macro's te maken. Een preprocessorrichtlijn moet de enige instructie op een regel zijn.

Null-context

De #nullable-preprocessorrichtlijn stelt de -aantekeningen en de -waarschuwingsvlaggen in binnen de -nullablecontext. Deze richtlijn bepaalt of null-aantekeningen effect hebben en of waarschuwingen voor null-baarheid worden gegeven. Elke vlag is uitgeschakeld of ingeschakeld.

Beide contexten kunnen worden opgegeven op projectniveau (buiten C#-broncode) waarbij het Nullable element wordt toegevoegd aan het PropertyGroup element. De #nullable-instructie bepaalt de aantekeningen en waarschuwingsvlagmen en heeft voorrang op de instellingen op projectniveau. Een richtlijn stelt de vlag in die het aanstuurt totdat een andere richtlijn deze overschrijft of tot het einde van het bronbestand.

Het effect van de richtlijnen is als volgt:

  • #nullable disable: hiermee stelt u de context nullable in uitgeschakeld.
  • #nullable enable: stelt de null-waarde context in met ingeschakeld.
  • #nullable restore: herstelt de null-context naar projectinstellingen.
  • #nullable disable annotations: stelt het annotatievlag in de nullable context in op uitgeschakeld.
  • #nullable enable annotations: stelt de aantekeningenvlag in de null-waarden context in op ingeschakelde.
  • #nullable restore annotations: Herstelt de annotatievlag in de null-context in de projectinstellingen.
  • #nullable disable warnings: stelt de waarschuwingsvlag in de null-context in op uitgeschakeld.
  • #nullable enable warnings: hiermee stelt u de waarschuwingsvlag in de null-context in op ingeschakelde.
  • #nullable restore warnings: hiermee herstelt u de waarschuwingsvlag in de null-context naar projectinstellingen.

Voorwaardelijke compilatie

U gebruikt vier preprocessorrichtlijnen voor het beheren van voorwaardelijke compilatie:

  • #if: Hiermee opent u een voorwaardelijke compilatie, waarbij code alleen wordt gecompileerd als het opgegeven symbool is gedefinieerd.
  • #elif: Sluit de voorgaande voorwaardelijke compilatie en opent een nieuwe voorwaardelijke compilatie op basis van of het opgegeven symbool is gedefinieerd.
  • #else: Hiermee sluit u de voorgaande voorwaardelijke compilatie en opent u een nieuwe voorwaardelijke compilatie als het vorige opgegeven symbool niet is gedefinieerd.
  • #endif: Hiermee sluit u de voorgaande voorwaardelijke compilatie.

Het buildsysteem is ook op de hoogte van vooraf gedefinieerde preprocessorsymbolen die verschillende doelframeworks in SDK-projecten vertegenwoordigen . Ze zijn handig bij het maken van toepassingen die zich kunnen richten op meer dan één .NET-versie.

Doelframeworks Symbolen Aanvullende symbolen
(beschikbaar in .NET 5+ SDK's)
Platformsymbolen (alleen beschikbaar)
wanneer u een besturingssysteemspecifieke TFM opgeeft)
.NET Framework NETFRAMEWORK, , NET481, , NET48, NET472, , NET471, NET47, NET462NET461NET46NET452NET451NET45NET40, NET35NET20 NET48_OR_GREATER, , NET472_OR_GREATER, , , , NET471_OR_GREATER, NET47_OR_GREATER, NET462_OR_GREATER, NET461_OR_GREATERNET46_OR_GREATERNET452_OR_GREATERNET451_OR_GREATERNET45_OR_GREATERNET40_OR_GREATERNET35_OR_GREATERNET20_OR_GREATER
.NET Standard NETSTANDARD, , NETSTANDARD2_1, NETSTANDARD2_0NETSTANDARD1_6, , NETSTANDARD1_5, NETSTANDARD1_4, , NETSTANDARD1_3NETSTANDARD1_2NETSTANDARD1_1NETSTANDARD1_0 NETSTANDARD2_1_OR_GREATER, , NETSTANDARD2_0_OR_GREATERNETSTANDARD1_6_OR_GREATER, NETSTANDARD1_5_OR_GREATER, , NETSTANDARD1_4_OR_GREATER, NETSTANDARD1_3_OR_GREATER, NETSTANDARD1_2_OR_GREATERNETSTANDARD1_1_OR_GREATERNETSTANDARD1_0_OR_GREATER
.NET 5+ (en .NET Core) NET, , NET9_0NET8_0, NET7_0, , NET6_0, , NET5_0NETCOREAPP, NETCOREAPP3_1, NETCOREAPP3_0NETCOREAPP2_2NETCOREAPP2_1NETCOREAPP2_0NETCOREAPP1_1,NETCOREAPP1_0 NET8_0_OR_GREATER, , NET7_0_OR_GREATERNET6_0_OR_GREATER, NET5_0_OR_GREATER, NETCOREAPP3_1_OR_GREATER, , NETCOREAPP3_0_OR_GREATER, , NETCOREAPP2_2_OR_GREATERNETCOREAPP2_1_OR_GREATERNETCOREAPP2_0_OR_GREATERNETCOREAPP1_1_OR_GREATERNETCOREAPP1_0_OR_GREATER ANDROID, , BROWSERIOS, MACCATALYST, , MACOS, , TVOSWINDOWS
[OS][version] (bijvoorbeeld IOS15_1),
[OS][version]_OR_GREATER (bijvoorbeeld IOS15_1_OR_GREATER)

Notitie

  • Versieloze symbolen worden gedefinieerd, ongeacht de versie die u wilt gebruiken.
  • Versiespecifieke symbolen worden alleen gedefinieerd voor de versie waarop u zich richt.
  • De <framework>_OR_GREATER symbolen worden gedefinieerd voor de versie waarop u zich richt en alle eerdere versies. Als u zich bijvoorbeeld richt op .NET Framework 2.0, worden de volgende symbolen gedefinieerd: NET20, NET20_OR_GREATER, NET11_OR_GREATERen NET10_OR_GREATER.
  • De NETSTANDARD<x>_<y>_OR_GREATER symbolen worden alleen gedefinieerd voor .NET Standard-doelen en niet voor doelen die .NET Standard implementeren, zoals .NET Core en .NET Framework.
  • Deze verschillen van de doelframework monikers (TFM's) die worden gebruikt door TargetFramework MSBuild en NuGet.

Notitie

Voor traditionele, niet-SDK-projecten moet u de symbolen voor voorwaardelijke compilatie handmatig configureren voor de verschillende doelframeworks in Visual Studio via de eigenschappenpagina's van het project.

Andere vooraf gedefinieerde symbolen zijn de DEBUG en TRACE constanten. U kunt de waarden die zijn ingesteld voor het project overschrijven met behulp van #define. Het DEBUG-symbool wordt bijvoorbeeld automatisch ingesteld, afhankelijk van uw buildconfiguratie-eigenschappen ('Foutopsporing' of 'Release').

De C#-compiler compileert de code tussen de #if instructie en #endif de instructie alleen als het opgegeven symbool is gedefinieerd of niet is gedefinieerd wanneer de ! operator niet wordt gebruikt. In tegenstelling tot C en C++ kan een numerieke waarde aan een symbool niet worden toegewezen. De #if-instructie in C# is Booleaans en test alleen of het symbool gedefinieerd is of niet. De volgende code wordt bijvoorbeeld gecompileerd wanneer DEBUG deze is gedefinieerd:

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

De volgende code wordt gecompileerd wanneer MYTEST deze niet is gedefinieerd:

#if !MYTEST
    Console.WriteLine("MYTEST is not defined");
#endif

U kunt de operators == (gelijkheid) en!=(ongelijkheid) gebruiken om te testen op de bool waarden true of false. true betekent dat het symbool is gedefinieerd. De instructie #if DEBUG heeft dezelfde betekenis als #if (DEBUG == true). U kunt de && (en), || (of)en ! (niet) operatoren gebruiken om te evalueren of er meerdere symbolen zijn gedefinieerd. U kunt ook symbolen en operatoren met haakjes groeperen.

In het volgende voorbeeld ziet u een complexe instructie waarmee uw code kan profiteren van nieuwere .NET-functies terwijl deze compatibel blijft met eerdere versies. Stel dat u een NuGet-pakket in uw code gebruikt, maar dat het pakket alleen .NET 6 en hoger ondersteunt, evenals .NET Standard 2.0 en hoger:

#if (NET6_0_OR_GREATER || NETSTANDARD2_0_OR_GREATER)
    Console.WriteLine("Using .NET 6+ or .NET Standard 2+ code.");
#else
    Console.WriteLine("Using older code that doesn't support the above .NET versions.");
#endif

#if, samen met de #else#elif#endif, , en #define#undef instructies, kunt u code opnemen of uitsluiten op basis van het bestaan van een of meer symbolen. Voorwaardelijke compilatie kan handig zijn bij het compileren van code voor een foutopsporingsbuild of bij het compileren van een specifieke configuratie.

#elif hiermee kunt u een samengestelde voorwaardelijke instructie maken. De #elif-expressie wordt geëvalueerd als noch de voorgaande #if, noch eventuele voorafgaande optionele #elif instructieexpressies tot truegeëvalueerd worden. Als een #elif expressie wordt geëvalueerd true, evalueert de compiler alle code tussen de #elif en de volgende voorwaardelijke instructie. Voorbeeld:

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

#else hiermee kunt u een samengestelde voorwaardelijke instructie maken, zodat, als geen van de expressies in de voorgaande #if of (optionele) #elif instructies wordt geëvalueerd true, de compiler alle code tussen #else en de volgende #endifevalueert. #endif(#endif) moet de volgende preprocessorrichtlijn na #elsezijn.

#endif geeft het einde van een voorwaardelijke richtlijn, die begon met de #if richtlijn.

In het volgende voorbeeld ziet u hoe u een MYTEST symbool voor een bestand definieert en vervolgens de waarden van de MYTEST en DEBUG symbolen test. De uitvoer van dit voorbeeld is afhankelijk van of u het project hebt gebouwd op de foutopsporings - of releaseconfiguratiemodus .

#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
    }
}

In het volgende voorbeeld ziet u hoe u kunt testen op verschillende doelframeworks, zodat u indien mogelijk nieuwere API's kunt gebruiken:

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

Symbolen definiëren

U gebruikt de volgende twee preprocessorrichtlijnen om symbolen voor voorwaardelijke compilatie te definiëren of ongedaan te maken:

  • #define: Definieer een symbool.
  • #undef: Een symbool ongedaan maken.

U gebruikt #define om een symbool te definiëren. Wanneer u het symbool gebruikt als de expressie die wordt doorgegeven aan de #if-instructie, resulteert de expressie in true, zoals in het volgende voorbeeld wordt weergegeven:

#define VERBOSE

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

Notitie

In C# moeten primitieve constanten worden gedefinieerd met behulp van het const trefwoord. Een const declaratie maakt een static lid dat tijdens runtime niet kan worden gewijzigd. De #define instructie kan niet worden gebruikt om constante waarden te declareren, zoals meestal wordt gedaan in C en C++. Als u verschillende dergelijke constanten hebt, kunt u overwegen om een afzonderlijke klasse Constanten te maken om deze vast te houden.

Symbolen kunnen worden gebruikt om voorwaarden voor compilatie op te geven. U kunt testen op het symbool met of #if#elif. U kunt ook de ConditionalAttribute functie gebruiken om voorwaardelijke compilatie uit te voeren. U kunt een symbool definiëren, maar u kunt geen waarde toewijzen aan een symbool. De #define instructie moet worden weergegeven in het bestand voordat u instructies gebruikt die niet ook preprocessorrichtlijnen zijn. U kunt ook een symbool definiëren met de compileroptie DefineConstants. U kunt een symbool ongedaan maken met #undef.

Regio's definiëren

U kunt regio's met code definiëren die in een overzicht kunnen worden samengevouwen met behulp van de volgende twee preprocessorrichtlijnen:

  • #region: Een regio starten.
  • #endregion: Een regio beëindigen.

#region hiermee kunt u een codeblok opgeven dat u kunt uitvouwen of samenvouwen wanneer u de overzichtsfunctie van de code-editor gebruikt. In langere codebestanden is het handig om een of meer regio's samen te vouwen of te verbergen, zodat u zich kunt richten op het deel van het bestand waaraan u momenteel werkt. In het volgende voorbeeld ziet u hoe u een regio definieert:

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

Een #region blok moet worden beëindigd met een #endregion richtlijn. Een #region blok kan niet overlappen met een #if blok. Een #region blok kan echter worden genest in een #if blok en een #if blok kan worden genest in een #region blok.

Fout- en waarschuwingsgegevens

U geeft de compiler opdracht om door de gebruiker gedefinieerde compilerfouten en waarschuwingen te genereren, en regelinformatie te beheren met behulp van de volgende instructies:

  • #error: Genereer een compilerfout met een opgegeven bericht.
  • #warning: Genereer een compilerwaarschuwing met een specifiek bericht.
  • #line: Wijzig het regelnummer dat wordt afgedrukt met compilerberichten.

#error hiermee kunt u een door de gebruiker gedefinieerde CS1029-fout genereren op basis van een specifieke locatie in uw code. Voorbeeld:

#error Deprecated code in this method.

Notitie

De compiler behandelt op een speciale manier en rapporteert #error version een compilerfout, CS8304, met een bericht met de gebruikte compiler- en taalversies.

#warning hiermee kunt u een waarschuwing van één compiler op CS1030 niveau één genereren vanaf een specifieke locatie in uw code. Voorbeeld:

#warning Deprecated code in this method.

#line hiermee kunt u de regelnummering van de compiler wijzigen en (optioneel) de bestandsnaamuitvoer wijzigen voor fouten en waarschuwingen.

In het volgende voorbeeld ziet u hoe u twee waarschuwingen rapporteert die zijn gekoppeld aan regelnummers. De #line 200 instructie dwingt dat het nummer van de volgende regel 200 is (hoewel de standaardwaarde #6 is) en tot de volgende #line instructie, wordt de bestandsnaam gerapporteerd als 'Speciaal'. De #line default-instructie retourneert de regelnummering naar de standaardnummering, waarmee de regels worden geteld die zijn hernummerd door de vorige richtlijn.

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;
    }
}

Compilatie produceert de volgende uitvoer:

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

De #line richtlijn kan worden gebruikt in een geautomatiseerde, tussenliggende stap in het bouwproces. Als bijvoorbeeld regels uit het oorspronkelijke broncodebestand zijn verwijderd, maar u toch de compiler uitvoer wilde laten genereren op basis van de oorspronkelijke regelnummering in het bestand, kunt u regels verwijderen en vervolgens de oorspronkelijke regelnummering simuleren met #line.

De #line hidden richtlijn verbergt de opeenvolgende regels van het foutopsporingsprogramma, zodat wanneer de ontwikkelaar de code doorloopt, alle regels tussen een #line hidden en de volgende #line richtlijn (ervan uitgaande dat het geen andere #line hidden richtlijn is) worden overgestapt. Deze optie kan ook worden gebruikt om ASP.NET onderscheid te maken tussen door de gebruiker gedefinieerde en door de machine gegenereerde code. Hoewel ASP.NET de primaire consument van deze functie is, is het waarschijnlijk dat meer brongeneratoren er gebruik van maken.

Een #line hidden richtlijn heeft geen invloed op bestandsnamen of regelnummers in foutrapportage. Als de compiler een fout in een verborgen blok vindt, rapporteert de compiler de huidige bestandsnaam en het regelnummer van de fout.

De #line filename instructie geeft de bestandsnaam op die u wilt weergeven in de compileruitvoer. Standaard wordt de werkelijke naam van het broncodebestand gebruikt. De bestandsnaam moet tussen dubbele aanhalingstekens ("") staan en moet een regelnummer volgen.

U kunt een nieuwe vorm van de #line-instructie gebruiken:

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

De onderdelen van dit formulier zijn:

  • (1, 1): De beginregel en kolom voor het eerste teken op de regel na de instructie. In dit voorbeeld wordt de volgende regel gerapporteerd als regel 1, kolom 1.
  • (5, 60): De eindlijn en kolom voor het gemarkeerde gebied.
  • 10: de kolomverrekening voor de #line richtlijn van kracht. In dit voorbeeld wordt de 10e kolom gerapporteerd als kolom één. De declaratie int b = 0; begint bij die kolom. Dit veld is optioneel. Als u dit weglaat, wordt de richtlijn van kracht op de eerste kolom.
  • "partial-class.cs": De naam van het uitvoerbestand.

In het voorgaande voorbeeld wordt de volgende waarschuwing gegenereerd:

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

Na het opnieuw toewijzen bevindt de variabele zich bop de eerste regel, op teken zes, van het bestand partial-class.cs.

Domeinspecifieke talen (DSLs) gebruiken deze indeling doorgaans om een betere toewijzing te bieden van het bronbestand naar de gegenereerde C#-uitvoer. Het meest voorkomende gebruik van deze uitgebreide #line instructie is het herleiden van waarschuwingen of fouten die worden weergegeven in een gegenereerd bestand naar het originele bestand. Denk bijvoorbeeld aan deze scheermespagina:

@page "/"
Time: @DateTime.NowAndThen

De eigenschap DateTime.Now is onjuist getypt als DateTime.NowAndThen. De gegenereerde C# voor dit razor-fragment ziet er als volgt uit:page.g.cs

  _builder.Add("Time: ");
#line (2, 6) - (2, 27) 15 "page.razor"
  _builder.Add(DateTime.NowAndThen);

De compileruitvoer voor het voorgaande codefragment is:

page.razor(2, 2, 2, 27)error CS0117: 'DateTime' does not contain a definition for 'NowAndThen'

Regel 2, kolom 6 in page.razor is de plaats waar de tekst @DateTime.NowAndThen begint, genoteerd door (2, 6) in de richtlijn. Dat bereik van @DateTime.NowAndThen eindigt op regel 2, kolom 27, genoteerd als (2, 27) in de richtlijn. De tekst voor DateTime.NowAndThen begint in kolom 15 van page.g.cs, vermeld door de 15 in de richtlijn. De compiler rapporteert de fout op de locatie in page.razor. De ontwikkelaar kan rechtstreeks naar de fout in de broncode navigeren, niet naar de gegenereerde bron.

Zie de functiespecificatie in de sectie over voorbeelden voor meer voorbeelden voor meer voorbeelden van deze indeling.

Pragmas

#pragma geeft de compiler speciale instructies voor de compilatie van het bestand waarin het wordt weergegeven. De compiler moet de pragma's ondersteunen die u gebruikt. Met andere woorden, u kunt geen aangepaste voorverwerkingsinstructies maken #pragma .

#pragma pragma-name pragma-arguments

Waar pragma-name is de naam van een herkende pragma en pragma-arguments de pragma-specifieke argumenten.

waarschuwing voor #pragma

#pragma warning kan bepaalde waarschuwingen in- of uitschakelen. De #pragma warning disable format en #pragma warning enable format bepalen hoe Codeblokken worden opgemaakt in Visual Studio.

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

Waar warning-list een door komma's gescheiden lijst met waarschuwingsnummers is, zoals 414, CS3021. Het voorvoegsel CS is optioneel. Als er geen waarschuwingsnummers zijn opgegeven, disable worden alle waarschuwingen uitgeschakeld en restore worden alle waarschuwingen ingeschakeld.

Notitie

Als u waarschuwingsnummers in Visual Studio wilt vinden, bouwt u uw project en zoekt u vervolgens naar de waarschuwingsnummers in het venster Uitvoer .

De disable actie wordt van kracht vanaf de volgende regel van het bronbestand. De waarschuwing wordt hersteld op de regel na de restore. Als het bestand niet restore aanwezig is, worden de waarschuwingen teruggezet naar de standaardstatus op de eerste regel van latere bestanden in dezelfde compilatie.

// 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()
    {
    }
}

Een andere vorm van de warning pragma schakelt Visual Studio-opmaakopdrachten uit of herstelt deze in codeblokken:

#pragma warning disable format
#pragma warning restore format

Opdrachten in Visual Studio-indeling wijzigen geen tekst in codeblokken waarin disable format van kracht is. Opmaakopdrachten, zoals Ctrl+K, Ctrl+D, veranderen die codegebieden niet. Deze pragma geeft u een prima controle over de visuele presentatie van uw code.

controlesom #pragma

Hiermee worden controlesommen gegenereerd voor bronbestanden om te helpen met foutopsporing ASP.NET pagina's.

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

Waar "filename" is de naam van het bestand dat controle op wijzigingen of updates vereist, "{guid}" is de GUID (Globally Unique Identifier) voor het hash-algoritme en "checksum_bytes" is de tekenreeks van hexadecimale cijfers die de bytes van de controlesom vertegenwoordigen. Moet een even aantal hexadecimale cijfers zijn. Een oneven aantal cijfers resulteert in een waarschuwing over de compilatietijd en de instructie wordt genegeerd.

Het foutopsporingsprogramma van Visual Studio maakt gebruik van een controlesom om ervoor te zorgen dat er altijd de juiste bron wordt gevonden. De compiler berekent de controlesom voor een bronbestand en verzendt vervolgens de uitvoer naar het PDB-bestand (program database). Het foutopsporingsprogramma gebruikt vervolgens de PDB om te vergelijken met de controlesom die wordt berekend voor het bronbestand.

Deze oplossing werkt niet voor ASP.NET projecten, omdat de berekende controlesom voor het gegenereerde bronbestand is in plaats van het .aspx bestand. Om dit probleem op te lossen, #pragma checksum biedt controlesomondersteuning voor ASP.NET pagina's.

Wanneer u een ASP.NET project maakt in Visual C#, bevat het gegenereerde bronbestand een controlesom voor het .aspx-bestand waaruit de bron wordt gegenereerd. De compiler schrijft deze informatie vervolgens naar het PDB-bestand.

Als de compiler geen instructie in het bestand vindt #pragma checksum , wordt de controlesom berekend en wordt de waarde naar het PDB-bestand geschreven.

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