Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Även om kompilatorn inte har någon separat förprocessor bearbetas de direktiv som beskrivs i det här avsnittet som om det fanns en. Du använder dem för att hjälpa till med villkorlig kompilering. Till skillnad från C- och C++-direktiv kan du inte använda dessa direktiv för att skapa makron. Ett förprocessordirektiv måste vara den enda instruktionen på en rad.
Filbaserade appar
              Filbaserade appar är program som kompileras och körs med hjälp av dotnet run Program.cs (eller valfri *.cs fil). C#-kompilatorn ignorerar dessa preprocessordirektiv, men byggsystemet parsar dem för att producera utdata. Dessa direktiv genererar varningar när de påträffas i en projektbaserad kompilering.
C#-kompilatorn ignorerar alla förprocessordirektiv som börjar med #: eller #!.
Med #! förprocessordirektivet kan unix-gränssnitt köra en C#-fil direkt med hjälp av dotnet run. Till exempel:
#!/usr/local/share/dotnet/dotnet run
Console.WriteLine("Hello");
Det föregående kodfragmentet informerar ett Unix-gränssnitt om att köra filen med ./usr/local/share/dotnet/dotnet run (Installationskatalogen dotnet för CLI kan vara olika i olika Unix- eller macOS-distributioner). Raden #! måste vara den första raden i filen och följande token är det program som ska köras. Du måste aktivera körningsbehörigheten (x) på C#-filen för den funktionen.
De #: direktiv som används i filbaserade appar är:
- #:sdk:- Den första instansen anger värdet för - <Project Sdk="value" />noden. Efterföljande instanser anger- <Sdk Name="value" Version="version" />noden. Versionen kan utelämnas (dvs. om den anges i global.json eller ingår i .NET SDK). Till exempel:- #:sdk Microsoft.NET.Sdk.Web #:sdk Aspire.AppHost.Sdk@9.4.1- De två föregående förprocessorerna översätts till: - <Project Sdk="Microsoft.NET.Sdk.Web" /> <Sdk Name="Aspire.AppHost.Sdk" Version="9.4.1" />
- #:property:- Instanser av - #:propertyöversätts till egenskapselement i en- <PropertyGroup>. En token för formuläret- Name=valuemåste följa- propertytoken. Följande exempeldirektiv är giltiga- propertytoken:- #:property TargetFramework=net11.0 #:property LangVersion=preview- De föregående två egenskaperna översätts till: - <TargetFramework>net11.0</TargetFramework> <LangVersion>preview</LangVersion>
- #:package:- Instanser av - #:packageöversätts till- PackageReferenceelement som inkluderar NuGet-paket med den angivna versionen till din fil. Till exempel:- #:package System.CommandLine@2.0.0-*- Föregående förprocessortoken översätts till: - <PackageReference Include="System.CommandLine" Version="2.0.0-*">
- #:project:- Instanser av - #:projectöversätts till- ProjectReferenceelement som inkluderar projektet med den angivna sökvägen till projektet. Till exempel:- #:project ../Path/To.Example- Föregående förprocessortoken översätts till: - <ProjectReference Include="../Path/To.Example/To.Example.csproj" />
Verktyg kan lägga till nya token enligt konventionen #: .
Nullbar kontext
I #nullable förprocessordirektivet anges anteckningar och varningsflaggor i nullbar kontext. Det här direktivet styr om ogiltiga anteckningar har effekt och om nullabilitetsvarningar ges. Varje flagga är antingen inaktiverad eller aktiverad.
Båda kontexterna kan anges på projektnivå (utanför C#-källkoden) och lägga till elementet Nullable i elementet PropertyGroup . 
              #nullable-direktivet styr antecknings- och varningsflaggor och har företräde framför inställningarna på projektnivå. Ett direktiv anger den flagga som det styr tills ett annat direktiv åsidosätter det, eller till slutet av källfilen.
Effekterna av direktiven är följande:
- 
              #nullable disable: Anger den nullbara kontexten inaktiverad.
- 
              #nullable enable: Anger det nullbara sammanhanget aktiverat.
- 
              #nullable restore: Återställer den nullbara kontexten till projektinställningarna.
- 
              #nullable disable annotations: Sätter annoteringsflaggan i nullbar kontext som inaktiverad.
- 
              #nullable enable annotations: Anger anteckningsflaggan i den nullbara kontexten till aktiverad.
- 
              #nullable restore annotations: Återställer annotationsflaggan i nullable-kontexten till projektinställningarna.
- 
              #nullable disable warnings: Sätter varningsflaggan i nullbar kontext till inaktiverad.
- 
              #nullable enable warnings: Sätter varningsflaggan i det nullbara sammanhanget till aktiverad.
- 
              #nullable restore warnings: Återställer varningsflaggan i den nullbara kontexten till projektinställningarna.
Villkorsstyrd kompilering
Du använder fyra förprocessordirektiv för att styra villkorlig kompilering:
- 
              #if: Öppnar en villkorlig kompilering, där kod endast kompileras om den angivna symbolen har definierats.
- 
              #elif: Stänger den föregående villkorliga kompileringen och öppnar en ny villkorlig kompilering baserat på om den angivna symbolen har definierats.
- 
              #else: Stänger den föregående villkorliga kompileringen och öppnar en ny villkorlig kompilering om den tidigare angivna symbolen inte har definierats.
- 
              #endif: Stänger den föregående villkorliga kompileringen.
Byggsystemet är också medvetet om fördefinierade förprocessorsymboler som representerar olika målramverk i SDK-liknande projekt. De är användbara när du skapar program som kan rikta in sig på mer än en .NET-version.
| Målramverk | Symboler | Ytterligare symboler (finns i .NET 5+ SDK:er) | Plattformssymboler (endast tillgängliga när du anger en OS-specifik TFM) | 
|---|---|---|---|
| .NET Framework | NETFRAMEWORK,NET481,NET48,NET472,NET471,NET47,NET462,NET461,NET46, ,NET452,NET451,NET45,NET40, ,NET35NET20 | 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_GREATERNET20_OR_GREATER | |
| .NET Standard | NETSTANDARD,NETSTANDARD2_1,NETSTANDARD2_0,NETSTANDARD1_6,NETSTANDARD1_5,NETSTANDARD1_4, ,NETSTANDARD1_3,NETSTANDARD1_2, ,NETSTANDARD1_1NETSTANDARD1_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_GREATERNETSTANDARD1_0_OR_GREATER | |
| .NET 5+ (och .NET Core) | NET,NET9_0,NET8_0,NET7_0,NET6_0,NET5_0,NETCOREAPP,NETCOREAPP3_1, ,NETCOREAPP3_0,NETCOREAPP2_2,NETCOREAPP2_1,NETCOREAPP2_0, ,NETCOREAPP1_1NETCOREAPP1_0 | NET9_0_OR_GREATER,NET8_0_OR_GREATER,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_GREATERNETCOREAPP1_0_OR_GREATER | ANDROID,BROWSER,IOS,MACCATALYST,MACOS, ,TVOS, ,WINDOWS[OS][version](till exempelIOS15_1),[OS][version]_OR_GREATER(till exempelIOS15_1_OR_GREATER) | 
Kommentar
- Versionslösa symboler definieras oavsett vilken version du riktar in dig på.
- Versionsspecifika symboler definieras endast för den version som du riktar in dig på.
- Symbolerna <framework>_OR_GREATERdefinieras för den version som du riktar in dig på och alla tidigare versioner. Om du till exempel riktar in dig på .NET Framework 2.0 definieras följande symboler:NET20,NET20_OR_GREATER,NET11_OR_GREATERochNET10_OR_GREATER.
- Symbolerna NETSTANDARD<x>_<y>_OR_GREATERdefinieras endast för .NET Standard-mål och inte för mål som implementerar .NET Standard, till exempel .NET Core och .NET Framework.
- Dessa skiljer sig från målramverksmonikers (TFM: er) som används av TargetFrameworkMSBuild och NuGet.
Kommentar
För traditionella, icke-SDK-liknande projekt måste du manuellt konfigurera de villkorliga kompileringssymbolerna för de olika målramverken i Visual Studio via projektets egenskapssidor.
Andra fördefinierade symboler är konstanterna DEBUG och TRACE . Du kan åsidosätta de värden som angetts för projektet med hjälp av #define. DEBUG-symbolen anges till exempel automatiskt beroende på byggkonfigurationsegenskaperna ("Felsökning" eller "Release"-läge).
C#-kompilatorn kompilerar koden mellan #if direktivet och #endif direktivet endast om den angivna symbolen definieras eller inte definieras när den ! inte används. Till skillnad från C och C++ kan ett numeriskt värde till en symbol inte tilldelas. 
              #if-instruktionen i C# är boolesk och testar endast om symbolen har definierats eller inte. Följande kod kompileras till exempel när DEBUG har definierats:
#if DEBUG
    Console.WriteLine("Debug version");
#endif
Följande kod kompileras när MYTEST inte  har definierats:
#if !MYTEST
    Console.WriteLine("MYTEST is not defined");
#endif
Du kan använda operatorerna == (likhet) och != (ojämlikhet) för att testa värdena booltrue eller false. 
              true betyder att symbolen har definierats. -instruktionen #if DEBUG har samma betydelse som #if (DEBUG == true). Du kan använda operatorerna && (och), || (eller)och ! (inte) för att utvärdera om flera symboler har definierats. Du kan också gruppera symboler och operatorer med parenteser.
I följande exempel visas ett komplext direktiv som gör att koden kan dra nytta av nyare .NET-funktioner samtidigt som den är bakåtkompatibel. Anta till exempel att du använder ett NuGet-paket i koden, men paketet stöder bara .NET 6 och uppåt, samt .NET Standard 2.0 och uppåt:
#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
              #ifMed , tillsammans med direktiven #else, #elif, #endif, #defineoch #undef kan du inkludera eller exkludera kod baserat på förekomsten av en eller flera symboler. Villkorsstyrd kompilering kan vara användbar när du kompilerar kod för en felsökningsversion eller vid kompilering för en specifik konfiguration.
              #elif låter dig skapa ett sammansatt villkorligt direktiv. Uttrycket #elif utvärderas om varken föregående #if eller några föregående, valfria #elif direktivuttryck utvärderas till true. Om ett #elif uttryck utvärderas till trueutvärderar kompilatorn all kod mellan #elif och nästa villkorsdirektiv. Till exempel:
#define VC7
//...
#if DEBUG
    Console.WriteLine("Debug build");
#elif VC7
    Console.WriteLine("Visual Studio 7");
#endif
              #else låter dig skapa ett sammansatt villkorligt direktiv, så att om inget av uttrycken i föregående #if eller (valfria) #elif direktiv utvärderas till trueutvärderar kompilatorn all kod mellan #else och nästa #endif. 
              #endif(#endif) måste vara nästa förprocessordirektiv efter #else.
              #endif anger slutet på ett villkorsdirektiv som började med #if direktivet.
I följande exempel visas hur du definierar en MYTEST symbol i en fil och sedan testar värdena för symbolerna MYTEST och DEBUG . Utdata från det här exemplet beror på om du har skapat projektet i  felsöknings- eller versionskonfigurationsläge.
#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
    }
}
I följande exempel visas hur du testar för olika målramverk så att du kan använda nyare API:er när det är möjligt:
public class MyClass
{
    static void Main()
    {
#if NET40
        WebClient _client = new WebClient();
#else
        HttpClient _client = new HttpClient();
#endif
    }
    //...
}
Definiera symboler
Du använder följande två förprocessordirektiv för att definiera eller odefiniera symboler för villkorlig kompilering:
- 
              #define: Definiera en symbol.
- 
              #undef: Odefiniera en symbol.
Du använder #define för att definiera en symbol. När du använder symbolen som uttrycket som skickas till #if-direktivet utvärderas uttrycket till true, som följande exempel visar:
#define VERBOSE
#if VERBOSE
   Console.WriteLine("Verbose output version");
#endif
Kommentar
I C# ska primitiva konstanter definieras med nyckelordet const . En const deklaration skapar en static medlem som inte kan ändras vid körning. Direktivet #define kan inte användas för att deklarera konstanta värden som normalt görs i C och C++. Om du har flera sådana konstanter kan du överväga att skapa en separat "Konstanter"-klass för att lagra dem.
Symboler kan användas för att ange villkor för kompilering. Du kan testa symbolen med antingen #if eller #elif. Du kan också använda ConditionalAttribute för att utföra villkorlig kompilering. Du kan definiera en symbol, men du kan inte tilldela ett värde till en symbol. Direktivet #define måste visas i filen innan du använder några instruktioner som inte också är förprocessordirektiv. Du kan också definiera en symbol med kompilatoralternativet DefineConstants . Du kan odefiniera en symbol med #undef.
Definiera regioner
Du kan definiera kodregioner som kan komprimeras i en disposition med hjälp av följande två förprocessordirektiv:
- 
              #region: Starta en region.
- 
              #endregion: Avsluta en region.
              #region låter dig ange ett kodblock som du kan expandera eller komprimera när du använder kodredigerarens dispositionsfunktion. I längre kodfiler är det praktiskt att komprimera eller dölja en eller flera regioner så att du kan fokusera på den del av filen som du arbetar med. I följande exempel visas hur du definierar en region:
#region MyClass definition
public class MyClass
{
    static void Main()
    {
    }
}
#endregion
Ett #region block måste avslutas med ett #endregion direktiv. Ett #region block kan inte överlappa med ett #if block. Ett block kan dock #region kapslas i ett #if block och ett #if block kan kapslas i ett #region block.
Fel- och varningsinformation
Du instruerar kompilatorn att generera användardefinierade kompilatorfel och varningar samt kontrollradsinformation med hjälp av följande direktiv:
- 
              #error: Generera ett kompilatorfel med ett angivet meddelande.
- 
              #warning: Generera en kompilatorvarning med ett specifikt meddelande.
- 
              #line: Ändra radnumret som skrivs ut med kompilatormeddelanden.
              #error låter dig generera ett användardefinierat CS1029-fel från en specifik plats i koden. Till exempel:
#error Deprecated code in this method.
Kommentar
Kompilatorn behandlar #error version på ett speciellt sätt och rapporterar ett kompilatorfel, CS8304, med ett meddelande som innehåller den använda kompilatorn och språkversionerna.
              #warning låter dig generera en cs1030 nivå en kompilatorvarning från en specifik plats i koden. Till exempel:
#warning Deprecated code in this method.
              #line låter dig ändra kompilatorns radnumrering och (valfritt) filnamnets utdata för fel och varningar.
I följande exempel visas hur du rapporterar två varningar som är associerade med radnummer. 
              #line 200-direktivet tvingar nästa rads tal att vara 200 (även om standardvärdet är #6), och fram till nästa #line direktiv rapporteras filnamnet som "Special". #line default-direktivet returnerar radnumreringen till standardnumreringen, vilket räknar de rader som omnumrerades av föregående direktiv.
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;
    }
}
Kompilering ger följande utdata:
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
Direktivet #line kan användas i ett automatiserat, mellanliggande steg i byggprocessen. Om rader till exempel har tagits bort från den ursprungliga källkodsfilen, men du fortfarande vill att kompilatorn ska generera utdata baserat på den ursprungliga radnumreringen i filen, kan du ta bort rader och sedan simulera den ursprungliga radnumreringen med #line.
Direktivet #line hidden döljer efterföljande rader från felsökningsprogrammet, så att när utvecklaren går igenom koden kommer alla rader mellan ett #line hidden och nästa #line direktiv (förutsatt att det inte är ett annat #line hidden direktiv) att kliva över. Det här alternativet kan också användas för att tillåta ASP.NET att skilja mellan användardefinierad och maskingenererad kod. Även om ASP.NET är den primära konsumenten av den här funktionen är det troligt att fler källgeneratorer använder den.
Ett #line hidden direktiv påverkar inte filnamn eller radnummer i felrapportering. Om kompilatorn hittar ett fel i ett dolt block rapporterar kompilatorn det aktuella filnamnet och radnumret för felet.
Direktivet #line filename anger det filnamn som du vill ska visas i kompilatorns utdata. Som standard används det faktiska namnet på källkodsfilen. Filnamnet måste vara inom dubbla citattecken ("") och måste följa ett radnummer.
Du kan använda en ny form av #line-direktivet:
#line (1, 1) - (5, 60) 10 "partial-class.cs"
/*34567*/int b = 0;
Komponenterna i det här formuläret är:
- 
              (1, 1): Startraden och kolumnen för det första tecknet på raden som följer direktivet. I det här exemplet rapporteras nästa rad som rad 1, kolumn 1.
- 
              (5, 60): Slutraden och kolumnen för den markerade regionen.
- 
              10: Kolumnförskjutningen för#lineatt direktivet ska börja gälla. I det här exemplet rapporteras den tionde kolumnen som kolumn ett. Deklarationenint b = 0;börjar vid den kolumnen. Det här fältet är valfritt. Om det utelämnas börjar direktivet gälla för den första kolumnen.
- 
              "partial-class.cs": Namnet på utdatafilen.
Föregående exempel skulle generera följande varning:
partial-class.cs(1,5,1,6): warning CS0219: The variable 'b' is assigned but its value is never used
Efter ommappning är variabeln , bpå den första raden, på tecken sex, i filen partial-class.cs.
Domänspecifika språk (DSL:er) använder vanligtvis det här formatet för att ge en bättre mappning från källfilen till de genererade C#-utdata. Den vanligaste användningen av det här utökade #line-direktivet är att mappa om varningar eller fel som visas i en genererad fil till den ursprungliga källan. Tänk dig till exempel den här rakbladssidan:
@page "/"
Time: @DateTime.NowAndThen
Egenskapen DateTime.Now angavs felaktigt som DateTime.NowAndThen. Det genererade C# för det här rakbladsfragmentet ser ut så här i page.g.cs:
  _builder.Add("Time: ");
#line (2, 6) - (2, 27) 15 "page.razor"
  _builder.Add(DateTime.NowAndThen);
Kompilatorns utdata för föregående kodfragment är:
page.razor(2, 2, 2, 27)error CS0117: 'DateTime' does not contain a definition for 'NowAndThen'
Rad 2, kolumn 6 i page.razor är den plats där texten @DateTime.NowAndThen börjar, vilket anges av (2, 6) i direktivet. Det intervallet för @DateTime.NowAndThen slutar på rad 2, kolumn 27, som anges av (2, 27) i direktivet. Texten för DateTime.NowAndThen börjar i kolumn 15 i page.g.cs, som anges av 15 i direktivet. Kompilatorn rapporterar felet på platsen i page.razor. Utvecklaren kan navigera direkt till felet i källkoden, inte till den genererade källan.
Mer information om det här formatet finns i funktionsspecifikationen i avsnittet om exempel.
Pragmas
              #pragma ger kompilatorn särskilda instruktioner för kompilering av filen där den visas. Kompilatorn måste ha stöd för de pragmas du använder. Med andra ord kan du inte använda #pragma för att skapa anpassade förbearbetningsinstruktioner.
- 
              #pragma warning: Aktivera eller inaktivera varningar.
- 
              #pragma checksum: Generera en kontrollsumma.
#pragma pragma-name pragma-arguments
Var pragma-name är namnet på en erkänd pragma och pragma-arguments är de pragma-specifika argumenten.
#pragma varning
              #pragma warning kan aktivera eller inaktivera vissa varningar. 
              #pragma warning disable format och #pragma warning enable format styr hur Visual Studio formaterar kodblock.
#pragma warning disable warning-list
#pragma warning restore warning-list
Där warning-list är en kommaavgränsad lista med varningsnummer, till exempel 414, CS3021. "CS"-prefixet är valfritt. När inga varningsnummer har angetts disable inaktiverar du alla varningar och restore aktiverar alla varningar.
Kommentar
Om du vill hitta varningsnummer i Visual Studio skapar du projektet och letar sedan efter varningsnumren i utdatafönstret.
Börjar disable gälla från och med nästa rad i källfilen. Varningen återställs på raden efter restore. Om det inte finns någon restore i filen återställs varningarna till standardtillståndet på den första raden i senare filer i samma kompilering.
// 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()
    {
    }
}
En annan form av warning pragma inaktiverar eller återställer Visual Studio-formateringskommandon i kodblock:
#pragma warning disable format
#pragma warning restore format
Visual Studio-formatkommandon ändrar inte text i kodblock där disable format gäller. Formatkommandon, till exempel Ctrl+ K, Ctrl + D, ändrar inte dessa kodregioner. Den här pragman ger dig fin kontroll över den visuella presentationen av koden.
#pragma kontrollsumma
Genererar kontrollsummor för källfiler för felsökning av ASP.NET sidor.
#pragma checksum "filename" "{guid}" "checksum bytes"
Där "filename" är namnet på filen som kräver övervakning för ändringar eller uppdateringar, "{guid}" är den globalt unika identifieraren (GUID) för hash-algoritmen och "checksum_bytes" är strängen med hexadecimala siffror som representerar kontrollsummans byte. Måste vara ett jämnt antal hexadecimala siffror. Ett udda antal siffror resulterar i en kompileringstidsvarning och direktivet ignoreras.
Visual Studio-felsökaren använder en kontrollsumma för att se till att den alltid hittar rätt källa. Kompilatorn beräknar kontrollsumman för en källfil och genererar sedan utdata till programdatabasfilen (PDB). Felsökningsprogrammet använder sedan PDB för att jämföra med den kontrollsumma som den beräknar för källfilen.
Den här lösningen fungerar inte för ASP.NET projekt, eftersom den beräknade kontrollsumman är för den genererade källfilen i stället för den .aspx filen. För att lösa det här problemet #pragma checksum tillhandahåller checksummor stöd för ASP.NET sidor.
När du skapar ett ASP.NET projekt i Visual C# innehåller den genererade källfilen en kontrollsumma för den .aspx fil som källan genereras från. Kompilatorn skriver sedan den här informationen till PDB-filen.
Om kompilatorn inte hittar något #pragma checksum direktiv i filen beräknas kontrollsumman och värdet skrivs till PDB-filen.
class TestClass
{
    static int Main()
    {
        #pragma checksum "file.cs" "{406EA660-64CF-4C82-B6F0-42D48172A799}" "ab007f1d23d9" // New checksum
    }
}