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 #nullable preprocesora ustawia kontekst adnotacji dopuszczającej wartość null i kontekst ostrzeżenia dopuszczającego 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żdy kontekst jest wyłączony lub włączony.

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 konteksty adnotacji i ostrzeżeń i ma pierwszeństwo przed ustawieniami na poziomie projektu. Dyrektywa ustawia konteksty, które kontroluje do momentu zastąpienia jej przez inną dyrektywę lub do końca pliku źródłowego.

Efekt dyrektyw jest następujący:

  • #nullable disable: ustawia konteksty adnotacji dopuszczające wartość null i ostrzeżenia na wyłączone.
  • #nullable enable: Ustawia konteksty adnotacji dopuszczające wartość null i ostrzeżenia, które mają być włączone.
  • #nullable restore: przywraca konteksty adnotacji dopuszczające wartość null i ostrzeżenia do ustawień projektu.
  • #nullable disable annotations: ustawia kontekst adnotacji dopuszczalnej wartości null na wyłączony.
  • #nullable enable annotations: Ustawia kontekst adnotacji dopuszczalnej wartości null do włączenia.
  • #nullable restore annotations: przywraca kontekst adnotacji dopuszczania wartości null do ustawień projektu.
  • #nullable disable warnings: ustawia kontekst ostrzeżenia dopuszczający wartość null na wyłączony.
  • #nullable enable warnings: ustawia kontekst ostrzeżenia dopuszczający wartość null, aby był włączony.
  • #nullable restore warnings: przywraca kontekst ostrzeżenia dopuszczający wartość null 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ą.

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. Instrukcja #if w języku C# jest wartością logiczną i sprawdza tylko, czy symbol został zdefiniowany, czy nie. 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). Aby ocenić, czy zdefiniowano wiele symboli, można użyć && operatorów (i), || (lub) i ! (nie ). Można również grupować symbole i operatory za pomocą nawiasów.

Poniżej przedstawiono 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.");
#elif
    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.

Dyrektywa warunkowa rozpoczynająca się od #if dyrektywy musi zostać jawnie zakończona dyrektywą #endif . #define umożliwia zdefiniowanie symbolu. Używając symbolu jako wyrażenia przekazanego #if do dyrektywy, wyrażenie zwraca wartość .true Możesz również zdefiniować symbol za pomocą opcji kompilatora DefineConstants . Możesz usunąć definicję symbolu za pomocą #undefpolecenia . Zakres symbolu utworzonego #define za pomocą to plik, w którym został zdefiniowany. Symbol zdefiniowany za pomocą elementu DefineConstants lub z elementem #define nie powoduje konfliktu ze zmienną o tej samej nazwie. Oznacza to, że nazwa zmiennej nie powinna być przekazywana do dyrektywy preprocesora, a symbol może być obliczany tylko przez dyrektywę preprocesora.

#elif umożliwia utworzenie złożonej dyrektywy warunkowej. Wyrażenie #elif zostanie obliczone, jeśli wyrażenie nie zostanie obliczone ani z poprzedniego #if , opcjonalnego wyrażenia #elif dyrektywy do truewartości . #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.

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, NET48, , , NET47NET462NET40NET471NET46NET35NET461NET452NET451NET45NET472NET20 NET48_OR_GREATER, NET472_OR_GREATER, , , NET462_OR_GREATERNET452_OR_GREATERNET47_OR_GREATERNET46_OR_GREATERNET35_OR_GREATERNET461_OR_GREATERNET451_OR_GREATERNET45_OR_GREATERNET40_OR_GREATERNET471_OR_GREATERNET20_OR_GREATER
.NET Standard NETSTANDARD, NETSTANDARD2_1, , , NETSTANDARD1_5NETSTANDARD1_1NETSTANDARD1_6NETSTANDARD1_4NETSTANDARD1_3NETSTANDARD1_2NETSTANDARD2_0NETSTANDARD1_0 NETSTANDARD2_1_OR_GREATER, NETSTANDARD2_0_OR_GREATER, , NETSTANDARD1_6_OR_GREATER, NETSTANDARD1_4_OR_GREATERNETSTANDARD1_5_OR_GREATER, NETSTANDARD1_3_OR_GREATER, , NETSTANDARD1_1_OR_GREATERNETSTANDARD1_2_OR_GREATERNETSTANDARD1_0_OR_GREATER
.NET 5+ (i .NET Core) NET, NET8_0, , , NET5_0NETCOREAPP3_0NET6_0NETCOREAPP3_1NETCOREAPP1_1NETCOREAPPNETCOREAPP2_2NETCOREAPP2_1NETCOREAPP2_0NET7_0NETCOREAPP1_0 NET8_0_OR_GREATER, NET7_0_OR_GREATER, , , NETCOREAPP3_1_OR_GREATERNETCOREAPP2_1_OR_GREATERNETCOREAPP1_1_OR_GREATERNET5_0_OR_GREATERNETCOREAPP3_0_OR_GREATERNETCOREAPP2_2_OR_GREATERNETCOREAPP2_0_OR_GREATERNET6_0_OR_GREATERNETCOREAPP1_0_OR_GREATER ANDROID, BROWSER, , IOS, MACOSMACCATALYST, , 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 właściwość MSBuild TargetFramework 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").

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 #if do dyrektywy, wyrażenie zwróci wartość true, jak pokazano w poniższym przykładzie:

#define VERBOSE

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

Uwaga

Dyrektywy #define nie można używać do deklarowania wartości stałych, jak zwykle odbywa się w języku C i C++. Stałe w języku C# najlepiej zdefiniować jako statyczne elementy członkowskie klasy lub struktury. 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 wymusza numer następnego wiersza na 200 (chociaż wartość domyślna to #6), a do następnej #line dyrektywy nazwa pliku zostanie zgłoszona jako "Specjalna". Dyrektywa #line default zwraca numerację wierszy do domyślnej numerowania, która zlicza wiersze, które zostały 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ę. Mimo że ASP.NET jest głównym konsumentem tej funkcji, prawdopodobnie więcej generatorów źródeł będzie z niego korzystać.

#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łosi 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ć poprzedzona numerem wiersza.

Począwszy od języka C# 10, możesz użyć nowej formy #line dyrektywy:

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

Składniki tego formularza to:

  • (1, 1): wiersz początkowy i kolumna pierwszego znaku w wierszu, który jest zgodny z dyrektywą. 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. W tym miejscu rozpoczyna się deklaracja int b = 0; . 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 #line dyrektywy jest ponowne mapowania ostrzeżeń lub błędów, które pojawiają się w wygenerowanym pliku w oryginalnym źródle. 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 obiekcie to miejsce, w page.razor którym rozpoczyna się tekst @DateTime.NowAndThen . To zanotowano (2, 6) w dyrektywie. Ta długość @DateTime.NowAndThen końca w wierszu 2, kolumna 27. To zanotowano w (2, 27) dyrektywie . Tekst dla DateTime.NowAndThen elementu rozpoczyna się w kolumnie 15 z page.g.cs. To zanotowano w 15 dyrektywie . Umieszczenie wszystkich argumentów razem, a kompilator zgłasza błąd w jego lokalizacji w pliku 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. Instrukcje muszą być obsługiwane przez kompilator. 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 warning-list
#pragma warning restore warning-list

Gdzie warning-list to rozdzielona przecinkami lista numerów ostrzeżeń. 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()
    {
    }
}

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