Udostępnij za pośrednictwem


Dyrektywy preprocesora języka C#

Chociaż kompilator nie ma oddzielnego preprocesora, dyrektywy opisane w tej sekcji są przetwarzane tak, jakby istniały. Są one używane do pomocy w kompilacji warunkowej. W przeciwieństwie do dyrektyw C i C++ nie można używać tych dyrektyw do tworzenia makr. Dyrektywa preprocesora musi być jedyną instrukcją w wierszu.

Kontekst dopuszczany do wartości null

Dyrektywa preprocesora ustawia adnotacje i flagi ostrzeżeń w kontekście dopuszczającym wartość null. Ta dyrektywa określa, czy adnotacje dopuszczające wartość null mają wpływ i czy są podane ostrzeżenia o wartości null. Każda flaga jest wyłączona lub włączona.

Oba konteksty można określić na poziomie projektu (poza kodem źródłowym języka C#), dodając Nullable element do PropertyGroup elementu. Dyrektywa #nullable kontroluje adnotację i flagi ostrzegawcze i ma pierwszeństwo przed ustawieniami na poziomie projektu. Dyrektywa ustawia flagę, którą kontroluje, dopóki inna dyrektywa nie zastąpi jej, lub do końca pliku źródłowego.

Efekt dyrektyw jest następujący:

  • #nullable disable: ustawia kontekst anulowalności wyłączony.
  • #nullable enable: ustawia kontekst dopuszczalny do wartości null .
  • #nullable restore: przywraca kontekst dopuszczalny do wartości null do ustawień projektu.
  • #nullable disable annotations: ustawia flagę adnotacji w kontekście dopuszczającym wartość null na wyłączoną.
  • #nullable enable annotations: ustawia flagę adnotacji w kontekście dopuszczającym wartości null, aby było włączone.
  • #nullable restore annotations: przywraca flagę adnotacji w kontekście dopuszczalności wartości null do ustawień projektu.
  • #nullable disable warnings: ustawia flagę ostrzegawczą w kontekście dopuszczania wartości null na wyłączoną.
  • #nullable enable warnings: ustawia flagę ostrzeżenia w kontekście obsługującym null, aby był włączony.
  • #nullable restore warnings: przywraca flagę ostrzegawczą w kontekście nullable do ustawień projektu.

Kompilacja warunkowa

Do kontrolowania kompilacji warunkowej używa się czterech dyrektyw preprocesora:

  • #if: Otwiera kompilację warunkową, w której kod jest kompilowany tylko wtedy, gdy określony symbol jest zdefiniowany.
  • #elif: zamyka poprzednią kompilację warunkową i otwiera nową kompilację warunkową na podstawie tego, czy określony symbol jest zdefiniowany.
  • #else: zamyka poprzednią kompilację warunkową i otwiera nową kompilację warunkową, jeśli poprzedni określony symbol nie jest zdefiniowany.
  • #endif: zamyka poprzednią kompilację warunkową.

System kompilacji jest również świadomy wstępnie zdefiniowanych symboli preprocesora reprezentujących różne platformy docelowe w projektach w stylu zestawu SDK. Są one przydatne podczas tworzenia aplikacji, które mogą być przeznaczone dla więcej niż jednej wersji platformy .NET.

Platformy docelowe Symbole Dodatkowe symbole
(dostępne w zestawach .NET 5+ SDK)
Symbole platformy (dostępne tylko
podczas określania programu TFM specyficznego dla systemu operacyjnego)
.NET Framework NETFRAMEWORK, NET481, , , NET48NET472NET471NET47NET462NET461NET46NET452NET451NET45NET40NET35NET20 NET48_OR_GREATER, NET472_OR_GREATER, , , NET471_OR_GREATERNET47_OR_GREATERNET462_OR_GREATERNET461_OR_GREATERNET46_OR_GREATERNET452_OR_GREATERNET451_OR_GREATERNET45_OR_GREATERNET40_OR_GREATERNET35_OR_GREATERNET20_OR_GREATER
.NET Standard NETSTANDARD, NETSTANDARD2_1, , , NETSTANDARD2_0NETSTANDARD1_6NETSTANDARD1_5NETSTANDARD1_4NETSTANDARD1_3NETSTANDARD1_2NETSTANDARD1_1NETSTANDARD1_0 NETSTANDARD2_1_OR_GREATER, NETSTANDARD2_0_OR_GREATER, , NETSTANDARD1_6_OR_GREATER, NETSTANDARD1_5_OR_GREATERNETSTANDARD1_4_OR_GREATER, NETSTANDARD1_3_OR_GREATER, , NETSTANDARD1_2_OR_GREATERNETSTANDARD1_1_OR_GREATERNETSTANDARD1_0_OR_GREATER
.NET 5+ (i .NET Core) NET, NET9_0, , , NET8_0NET7_0NET6_0NET5_0NETCOREAPPNETCOREAPP3_1NETCOREAPP3_0NETCOREAPP2_2NETCOREAPP2_1NETCOREAPP2_0NETCOREAPP1_1NETCOREAPP1_0 NET8_0_OR_GREATER, NET7_0_OR_GREATER, , , NET6_0_OR_GREATERNET5_0_OR_GREATERNETCOREAPP3_1_OR_GREATERNETCOREAPP3_0_OR_GREATERNETCOREAPP2_2_OR_GREATERNETCOREAPP2_1_OR_GREATERNETCOREAPP2_0_OR_GREATERNETCOREAPP1_1_OR_GREATERNETCOREAPP1_0_OR_GREATER ANDROID, BROWSER, , IOS, MACCATALYSTMACOS, , TVOS, , WINDOWS
[OS][version] (na przykład IOS15_1),
[OS][version]_OR_GREATER (na przykład IOS15_1_OR_GREATER)

Uwaga

  • Symbole bez wersji są definiowane niezależnie od docelowej wersji.
  • Symbole specyficzne dla wersji są definiowane tylko dla docelowej wersji.
  • Symbole <framework>_OR_GREATER są definiowane dla docelowej wersji i wszystkich wcześniejszych wersji. Jeśli na przykład używasz platformy .NET Framework 2.0, zdefiniowane są następujące symbole: NET20, , NET20_OR_GREATERNET11_OR_GREATERi NET10_OR_GREATER.
  • Symbole NETSTANDARD<x>_<y>_OR_GREATER są definiowane tylko dla obiektów docelowych platformy .NET Standard, a nie dla obiektów docelowych implementujących platformę .NET Standard, takich jak .NET Core i .NET Framework.
  • Różnią się one od obiektów docelowych monikers (TFMs) używanych przez TargetFramework MSBuild i NuGet.

Uwaga

W przypadku tradycyjnych projektów innych niż zestaw SDK należy ręcznie skonfigurować symbole kompilacji warunkowej dla różnych platform docelowych w programie Visual Studio za pośrednictwem stron właściwości projektu.

Inne wstępnie zdefiniowane symbole obejmują DEBUG stałe i .TRACE Możesz zastąpić wartości ustawione dla projektu przy użyciu polecenia #define. Na przykład symbol DEBUG jest automatycznie ustawiany w zależności od właściwości konfiguracji kompilacji ("Debugowanie" lub "Wydanie").

Kompilator języka C# kompiluje kod między dyrektywą #if a #endif dyrektywą tylko wtedy, gdy określony symbol jest zdefiniowany, lub nie jest zdefiniowany, gdy ! nie jest używany operator. W przeciwieństwie do języka C i C++, nie można przypisać wartości liczbowej do symbolu. Wyrażenie #if w języku C# jest logiczne i sprawdza tylko, czy symbol jest zdefiniowany. Na przykład następujący kod jest kompilowany, gdy DEBUG jest zdefiniowany:

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

Poniższy kod jest kompilowany, gdy MYTEST nie jest zdefiniowany:

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

Możesz użyć operatorów == (równości) i != (nierówności), aby przetestować bool wartości true lub false. true oznacza, że symbol jest zdefiniowany. Instrukcja #if DEBUG ma takie samo znaczenie jak #if (DEBUG == true). Możesz użyć operatorów && (i), || (lub)i ! (nie), aby ocenić, czy zdefiniowano wiele symboli. Można również grupować symbole i operatory za pomocą nawiasów.

Poniższy przykład przedstawia złożoną dyrektywę, która umożliwia kodowi korzystanie z nowszych funkcji platformy .NET przy zachowaniu zgodności z poprzednimi wersjami. Załóżmy na przykład, że używasz pakietu NuGet w kodzie, ale pakiet obsługuje tylko platformę .NET 6 i w górę, a także program .NET Standard 2.0 i nowszy:

#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, wraz z dyrektywami #else, #elif, #endif, #definei #undef umożliwia dołączanie lub wykluczanie kodu na podstawie istnienia co najmniej jednego symbolu. Kompilacja warunkowa może być przydatna podczas kompilowania kodu kompilacji debugowania lub kompilowania dla określonej konfiguracji.

#elif umożliwia utworzenie złożonej dyrektywy warunkowej. Wyrażenie #elif jest oceniane, jeśli ani poprzednie wyrażenie dyrektywy #if, ani poprzednie, opcjonalne wyrażenie dyrektywy #elif nie równa się true. #elif Jeśli wyrażenie zwróci wartość true, kompilator oblicza cały kod między #elif i następną dyrektywą warunkową. Na przykład:

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

#else pozwala utworzyć złożoną dyrektywę warunkową, tak aby, jeśli żadne z wyrażeń w poprzednich #if lub (opcjonalnych) #elif dyrektywach daje wartość true, kompilator oceni cały kod między #else i następnym #endif. #endif(#endif) musi być następną dyrektywą preprocesora po #else.

#endif określa koniec dyrektywy warunkowej, która rozpoczęła się od #if dyrektywy.

W poniższym przykładzie pokazano, jak zdefiniować MYTEST symbol w pliku, a następnie przetestować wartości MYTEST symboli i DEBUG . Dane wyjściowe tego przykładu zależą od tego, czy projekt został utworzony w trybie konfiguracji debugowania , czy wydania .

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

W poniższym przykładzie pokazano, jak przetestować różne platformy docelowe, aby w miarę możliwości używać nowszych interfejsów API:

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

Definiowanie symboli

Do definiowania lub niezdefiniowania symboli kompilacji warunkowej są używane następujące dwie dyrektywy preprocesora:

  • #define: Zdefiniuj symbol.
  • #undef: Niezdefiniuj symbol.

#define Służy do definiowania symbolu. Jeśli używasz symbolu jako wyrażenia przekazanego do dyrektywy #if, wyrażenie zwraca wartość true, jak pokazano w poniższym przykładzie:

#define VERBOSE

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

Uwaga

W języku C# stałe pierwotne powinny być definiowane przy użyciu słowa kluczowego const . Deklaracja const tworzy element członkowski static , którego nie można modyfikować w czasie wykonywania. Dyrektywy #define nie można używać do deklarowania wartości stałych, ponieważ zwykle odbywa się w językach C i C++. Jeśli masz kilka takich stałych, rozważ utworzenie oddzielnej klasy "Stałe", aby je przechowywać.

Symbole mogą służyć do określania warunków kompilacji. Możesz przetestować symbol za pomocą symbolu #if lub #elif. Można również użyć polecenia ConditionalAttribute , aby wykonać kompilację warunkową. Można zdefiniować symbol, ale nie można przypisać wartości do symbolu. Dyrektywa #define musi pojawić się w pliku przed użyciem instrukcji, które nie są również dyrektywami preprocesora. Możesz również zdefiniować symbol za pomocą opcji kompilatora DefineConstants . Możesz usunąć definicję symbolu za pomocą #undefpolecenia .

Definiowanie regionów

Regiony kodu, które można zwinąć w konspekcie, można zdefiniować przy użyciu następujących dwóch dyrektyw preprocesora:

  • #region: Uruchom region.
  • #endregion: Kończ region.

#region Umożliwia określenie bloku kodu, który można rozwinąć lub zwinąć podczas korzystania z funkcji tworzenia konspektowania edytora kodu. W dłuższych plikach kodu wygodne jest zwinięcie lub ukrycie jednego lub większej liczby regionów, dzięki czemu można skupić się na części aktualnie działającego pliku. W poniższym przykładzie pokazano, jak zdefiniować region:

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

Blok #region musi zostać zakończony dyrektywą #endregion . Blok #region nie może nakładać się na #if blok. #region Blok można jednak zagnieżdżać w #if bloku, a #if blok można zagnieżdżać w #region bloku.

Informacje o błędach i ostrzeżeniach

Poinstruuj kompilator, aby wygenerował błędy i ostrzeżenia kompilatora zdefiniowanego przez użytkownika oraz informacje o wierszu sterowania przy użyciu następujących dyrektyw:

  • #error: Wygeneruj błąd kompilatora z określonym komunikatem.
  • #warning: Wygeneruj ostrzeżenie kompilatora z określonym komunikatem.
  • #line: Zmień numer wiersza wydrukowany za pomocą komunikatów kompilatora.

#error Umożliwia wygenerowanie błędu zdefiniowanego przez użytkownika CS1029 z określonej lokalizacji w kodzie. Na przykład:

#error Deprecated code in this method.

Uwaga

Kompilator traktuje #error version w specjalny sposób i zgłasza błąd kompilatora CS8304 z komunikatem zawierającym używany kompilator i wersje językowe.

#warning umożliwia wygenerowanie ostrzeżenia kompilatora na poziomie CS1030 z określonej lokalizacji w kodzie. Na przykład:

#warning Deprecated code in this method.

#line Umożliwia zmodyfikowanie numerowania wiersza kompilatora i (opcjonalnie) danych wyjściowych nazwy pliku pod kątem błędów i ostrzeżeń.

W poniższym przykładzie pokazano, jak zgłosić dwa ostrzeżenia skojarzone z numerami wierszy. Dyrektywa #line 200 ustala numer następnej linii na 200 (chociaż wartość domyślna to #6), a do następnej dyrektywy #line nazwa pliku będzie raportowana jako "Special". Dyrektywa #line default przywraca numerację linii do ustawień domyślnych, które uwzględniają linie ponownie numerowane przez poprzednią dyrektywę.

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

Kompilacja generuje następujące dane wyjściowe:

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

Dyrektywa #line może być używana w zautomatyzowanym, pośrednim kroku procesu kompilacji. Jeśli na przykład wiersze zostały usunięte z oryginalnego pliku kodu źródłowego, ale nadal chcesz, aby kompilator wygenerował dane wyjściowe na podstawie oryginalnego numerowania wiersza w pliku, można usunąć wiersze, a następnie zasymulować oryginalne numerowanie wierszy za pomocą #linepolecenia .

Dyrektywa #line hidden ukrywa kolejne wiersze z debugera, tak aby po przejściu przez dewelopera przez kod wszelkie wiersze między dyrektywą a #line hidden następną #line (zakładając, że nie jest to inna #line hidden dyrektywa) zostaną zastąpione. Tej opcji można również użyć, aby umożliwić ASP.NET rozróżnianie kodu zdefiniowanego przez użytkownika i wygenerowanego przez maszynę. Chociaż ASP.NET jest głównym odbiorcą tej funkcji, prawdopodobnie więcej generatorów źródeł korzysta z niej.

#line hidden Dyrektywa nie ma wpływu na nazwy plików ani numery wierszy w raportowaniu błędów. Oznacza to, że jeśli kompilator znajdzie błąd w ukrytym bloku, kompilator zgłasza bieżącą nazwę pliku i numer wiersza błędu.

Dyrektywa #line filename określa nazwę pliku, którą chcesz wyświetlić w danych wyjściowych kompilatora. Domyślnie jest używana rzeczywista nazwa pliku kodu źródłowego. Nazwa pliku musi być w podwójnym cudzysłowie ("") i musi być zgodna z numerem wiersza.

Możesz użyć nowej formy dyrektywy #line:

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

Składniki tego formularza to:

  • (1, 1): Linia początkowa i kolumna pierwszego znaku w linii następującej po dyrektywie. W tym przykładzie następny wiersz zostanie zgłoszony jako wiersz 1, kolumna 1.
  • (5, 60): wiersz końcowy i kolumna dla oznaczonego regionu.
  • 10: przesunięcie kolumny dla #line dyrektywy, która ma obowiązywać. W tym przykładzie 10 kolumna zostanie zgłoszona jako kolumna pierwsza. Deklaracja int b = 0; zaczyna się od tej kolumny. To pole jest opcjonalne. W przypadku pominięcia dyrektywa ma wpływ na pierwszą kolumnę.
  • "partial-class.cs": nazwa pliku wyjściowego.

Powyższy przykład wygenerowałby następujące ostrzeżenie:

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

Po ponownym mapowania zmiennej b, znajduje się w pierwszym wierszu, w znaku sześciu znaków pliku partial-class.cs.

Języki specyficzne dla domeny (DSL) zwykle używają tego formatu, aby zapewnić lepsze mapowanie z pliku źródłowego do wygenerowanych danych wyjściowych języka C#. Najczęstszym zastosowaniem tej rozszerzonej dyrektywy #line jest ponowne mapowanie ostrzeżeń lub błędów, które pojawiają się w wygenerowanym pliku do oryginalnego źródła. Rozważmy na przykład tę stronę razor:

@page "/"
Time: @DateTime.NowAndThen

Właściwość DateTime.Now została wpisana niepoprawnie jako DateTime.NowAndThen. Wygenerowany kod C# dla tego fragmentu kodu razor wygląda następująco:page.g.cs

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

Dane wyjściowe kompilatora dla poprzedniego fragmentu kodu to:

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

Wiersz 2, kolumna 6 w page.razor to miejsce, w którym rozpoczyna się tekst @DateTime.NowAndThen, oznaczany przez (2, 6) w dyrektywie. Zakres @DateTime.NowAndThen kończy się w wierszu 2, w kolumnie 27, co jest zaznaczone przez (2, 27) w dyrektywie. Tekst dla DateTime.NowAndThen zaczyna się w kolumnie 15 na page.g.cs, co jest zaznaczone przez 15 w dyrektywie. Kompilator zgłasza błąd w lokalizacji w page.razor. Deweloper może przejść bezpośrednio do błędu w kodzie źródłowym, a nie do wygenerowanego źródła.

Aby wyświetlić więcej przykładów tego formatu, zobacz specyfikację funkcji w sekcji dotyczącej przykładów.

Pragma — dyrektywy

#pragma udostępnia kompilatorowi specjalne instrukcje dotyczące kompilacji pliku, w którym się pojawia. Kompilator musi obsługiwać używane pragmas. Innymi słowy, nie można użyć #pragma polecenia do tworzenia niestandardowych instrukcji przetwarzania wstępnego.

#pragma pragma-name pragma-arguments

Gdzie pragma-name jest nazwą rozpoznanej pragma i pragma-arguments jest argumentami specyficznymi dla pragma.

#pragma warning

#pragma warning może włączać lub wyłączać niektóre ostrzeżenia. #pragma warning disable format i #pragma warning enable format kontrolują formatowanie bloków kodu w programie Visual Studio.

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

Gdzie warning-list to rozdzielona przecinkami lista numerów ostrzeżeń, takich jak 414, CS3021. Prefiks "CS" jest opcjonalny. Jeśli nie określono żadnych numerów ostrzeżeń, disable wyłącza wszystkie ostrzeżenia i restore włącza wszystkie ostrzeżenia.

Uwaga

Aby znaleźć numery ostrzeżeń w programie Visual Studio, skompiluj projekt, a następnie poszukaj numerów ostrzeżeń w oknie Dane wyjściowe .

Element disable zaczyna się od następnego wiersza pliku źródłowego. Ostrzeżenie zostanie przywrócone w wierszu po .restore Jeśli nie restore ma w pliku, ostrzeżenia zostaną przywrócone do stanu domyślnego w pierwszym wierszu wszystkich późniejszych plików w tej samej kompilacji.

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

Inna forma warning pragma wyłącza lub przywraca polecenia formatowania programu Visual Studio w blokach kodu:

#pragma warning disable format
#pragma warning restore format

Polecenia formatu programu Visual Studio nie modyfikują tekstu w blokach kodu, w których działa disable format. Polecenia formatowania, takie jak Ctrl+Ki Ctrl+D, nie modyfikują tych regionów kodu. Ta pragma zapewnia precyzyjną kontrolę nad wizualną prezentacją kodu.

#pragma checksum

Generuje sumy kontrolne dla plików źródłowych, aby ułatwić debugowanie ASP.NET stron.

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

Gdzie "filename" jest nazwą pliku, który wymaga monitorowania zmian lub aktualizacji, "{guid}" jest globalnie unikatowy identyfikator (GUID) algorytmu skrótu i "checksum_bytes" jest ciągiem cyfr szesnastkowych reprezentujących bajty sumy kontrolnej. Musi być parzystą liczbą cyfr szesnastkowej. Liczba nieparzysta cyfr powoduje wyświetlenie ostrzeżenia w czasie kompilacji, a dyrektywa jest ignorowana.

Debuger programu Visual Studio używa sumy kontrolnej, aby upewnić się, że zawsze znajduje odpowiednie źródło. Kompilator oblicza sumę kontrolną dla pliku źródłowego, a następnie emituje dane wyjściowe do pliku bazy danych programu (PDB). Debuger następnie używa pdB do porównania z sumą kontrolną, którą oblicza dla pliku źródłowego.

To rozwiązanie nie działa w przypadku projektów ASP.NET, ponieważ obliczona suma kontrolna dotyczy wygenerowanego pliku źródłowego, a nie pliku .aspx. Aby rozwiązać ten problem, #pragma checksum zapewnia obsługę sumy kontrolnej dla stron ASP.NET.

Podczas tworzenia projektu ASP.NET w języku Visual C# wygenerowany plik źródłowy zawiera sumę kontrolną dla pliku .aspx, z którego jest generowane źródło. Kompilator zapisuje te informacje w pliku PDB.

Jeśli kompilator nie znajdzie dyrektywy w pliku, oblicza sumę kontrolną #pragma checksum i zapisuje wartość w pliku PDB.

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