コンパイラに個別のプリプロセッサはありませんが、このセクションに示すディレクティブは、ある場合と同じように処理されます。 これらを使用すると、条件付きコンパイルに役立ちます。 C や C++ のディレクティブとは異なり、マクロを作成するのにこれらのディレクティブを使用することはできません。 プリプロセッサ ディレクティブは、行の唯一の命令である必要があります。
ファイル ベースのプログラム
ファイル ベースのプログラム は、 dotnet run Program.cs
(または任意の *.cs
ファイル) を使用してコンパイルおよび実行されるプログラムです。 C# コンパイラはこれらのプリプロセッサ ディレクティブを無視しますが、ビルド システムはそれらを解析して出力を生成します。 これらのディレクティブは、プロジェクト ベースのコンパイルで発生すると警告を生成します。
C# コンパイラは、 #:
または #!
で始まるプリプロセッサ ディレクティブを無視します。
#!
プリプロセッサ ディレクティブを使用すると、unix シェルで dotnet run
を使用して C# ファイルを直接実行できます。 たとえば:
#!/usr/local/share/dotnet/dotnet run
Console.WriteLine("Hello");
上記のコード スニペットは、 /usr/local/share/dotnet/dotnet run
を使用してファイルを実行するように Unix シェルに通知します。 ( dotnet
のインストール ディレクトリCLI は、Unix または macOS ディストリビューションによって異なる場合があります)。 #!
行はファイルの最初の行である必要があり、次のトークンは実行するプログラムです。 その機能の C# ファイルに対して 実行 (x
) アクセス許可を有効にする必要があります。
ファイル ベースのプログラムで使用される #:
ディレクティブは次のとおりです。
#:sdk
:最初のインスタンスは、
<Project Sdk="value" />
ノードの値を指定します。 後続のインスタンスは、<Sdk Name="value" Version="version" />
ノードを指定します。 バージョンは省略できます。 たとえば:#:sdk Microsoft.NET.Sdk.Web
#:property
:#:property
のインスタンスは、<PropertyGroup>
のプロパティ要素に変換されます。 フォームName=value
のトークンは、property
トークンに従う必要があります。 次のディレクティブ例は、有効なproperty
トークンです。#:property TargetFramework=net11.0 #:property LangVersion=preview
上記の 2 つのプロパティは、次のように変換されます。
<TargetFramework>net11.0</TargetFramework> <LangVersion>preview</LangVersion>
#:package
:#:package
のインスタンスは、指定したバージョンの NuGet パッケージをファイルに含めるために、PackageReference
要素に変換されます。 たとえば:#:package System.CommandLine@2.0.0-*
上記のプリプロセッサ トークンは、次のように変換されます。
<PackageReference Include="System.CommandLine" Version="2.0.0-*">
ツールは、 #:
規則に従って新しいトークンを追加できます。
Null 許容コンテキスト
#nullable
プリプロセッサ ディレクティブは、null 許容コンテキストに注釈および警告フラグを設定します。 このディレクティブでは、Null 許容注釈の効果があるかどうか、および NULL 値の許容の警告が与えられるかどうかが制御されます。 各フラグは無効 か、有効です。
どちらのコンテキストもプロジェクト レベル (C# ソース コードの外部) で指定することができ、Nullable
要素が PropertyGroup
要素に追加されます。 #nullable
ディレクティブは、注釈フラグと警告フラグを制御し、プロジェクト レベルの設定よりも優先されます。 ディレクティブは、別のディレクティブによってオーバーライドされるまで、またはソース ファイルの最後まで制御するフラグを設定します。
ディレクティブの効果は次のとおりです。
-
#nullable disable
: null 許容コンテキストを無効に設定します。 -
#nullable enable
: null 許容コンテキストを有効に設定します。 #nullable restore
: Nullable コンテキストをプロジェクト設定に復元します。-
#nullable disable annotations
: null 許容コンテキスト内の注釈フラグを無効に設定します。 -
#nullable enable annotations
: null 許容コンテキスト内の注釈フラグを有効に設定します。 -
#nullable restore annotations
: null 許容コンテキスト内の注釈フラグをプロジェクト設定に復元します。 -
#nullable disable warnings
: null 許容コンテキスト内の警告フラグを無効に設定します。 -
#nullable enable warnings
: null 許容コンテキスト内の警告フラグを有効に設定します。 -
#nullable restore warnings
: null 許容警告コンテキスト内の警告フラグをプロジェクト設定に復元します。
条件付きコンパイル
次の 4 つのプリプロセッサ ディレクティブを使用して、条件付きコンパイルを制御します。
-
#if
: 条件付きコンパイルを開きます。コードは、指定されたシンボルが定義されている場合にのみコンパイルされます。 -
#elif
: 前の条件付きコンパイルを閉じ、指定されたシンボルが定義されているかどうかに基づいて、新しい条件付きコンパイルを開きます。 -
#else
: 前の条件付きコンパイルを閉じ、前に指定されたシンボルが定義されていない場合は、新しい条件付きコンパイルを開きます。 -
#endif
: 前の条件付きコンパイルを閉じます。
ビルド システムは、SDK 型プロジェクトの各種ターゲット フレームワークを表す、定義済みプリプロセッサ シンボルも認識します。 これは、複数の .NET バージョンをターゲットとできるアプリケーションを作成する場合に役立ちます。
ターゲット フレームワーク | シンボル | 追加のシンボル (.NET 5 以降の SDK で使用可能) |
プラットフォーム シンボル (OS 固有の TFM を指定する場合のみ使用可能) |
---|---|---|---|
.NET Framework | NETFRAMEWORK 、 NET481 、 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+ (および .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_1 、NETCOREAPP1_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_GREATER 、NETCOREAPP1_0_OR_GREATER |
ANDROID 、 BROWSER 、 IOS 、 MACCATALYST 、 MACOS 、 TVOS 、 WINDOWS 、[OS][version] (たとえば、IOS15_1 )、[OS][version]_OR_GREATER (たとえば、IOS15_1_OR_GREATER ) |
注意
- バージョンレスのシンボルは、ターゲットとするバージョンに関係なく定義されます。
- バージョン固有のシンボルは、対象とするバージョンに対してのみ定義されます。
<framework>_OR_GREATER
シンボルは、ターゲットとするバージョンとそれ以前のすべてのバージョンに対して定義されます。 たとえば、.NET Framework 2.0 をターゲットとする場合、NET20
、NET20_OR_GREATER
、NET11_OR_GREATER
、NET10_OR_GREATER
のシンボルが定義されます。NETSTANDARD<x>_<y>_OR_GREATER
シンボルは.NET Standard ターゲットにのみ定義され、.NET Core や .NET Framework などの .NET Standard を実装するターゲットには定義されません。- これらは、MSBuild
TargetFramework
プロパティと NuGet で使用されるターゲット フレームワーク モニカー (TFM) とは異なります。
注意
従来の非 SDK スタイルのプロジェクトでは、プロジェクトの [プロパティ] ページを使用して、Visual Studio のさまざまなターゲット フレームワークの条件付きコンパイル シンボルを手動で構成する必要があります。
他の定義済みシンボルには、DEBUG
および TRACE
定数が含まれます。 #define
を使用して、プロジェクトに設定された値をオーバーライドできます。 たとえば、DEBUG シンボルは、ビルド構成プロパティ ("デバッグ" モードまたは "リリース" モード) に応じて自動的に設定されます。
C# コンパイラでは、指定されたシンボルが定義されている場合、または !
not 演算子が使用されるときに定義されていない場合にのみ、#if
ディレクティブと #endif
ディレクティブの間のコードをコンパイルします。 C や C++ とは異なり、シンボルに数値を代入することはできません。 C# の #if
ステートメントはブール値であり、シンボルが定義されているかどうかをテストするだけです。 たとえば、次のコードは、DEBUG
が定義されているときにコンパイルされます。
#if DEBUG
Console.WriteLine("Debug version");
#endif
次のコードは、MYTEST
が定義されていない場合にコンパイルされます。
#if !MYTEST
Console.WriteLine("MYTEST is not defined");
#endif
bool
値true
または false
をテストするために、演算子 ==
(等式) および !=
(不等式) を使用することができます。 true
は、シンボルが定義されていることを意味します。 ステートメント #if DEBUG
と #if (DEBUG == true)
の意味は同じです。 &&
(および)、||
(または)、および !
(not) 演算子を使用して、複数のシンボルが定義されているかどうかを評価できます。 シンボルと演算子は、かっこを使用してグループ化できます。
次の例は、下位互換性を維持しながら、コードで新しい .NET 機能を利用できるようにする複雑なディレクティブを示しています。 たとえば、コードで NuGet パッケージを使用しているが、そのパッケージでは .NET 6 以降と .NET Standard 2.0 以降のみをサポートしているとします。
#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
と #else
、#elif
、#endif
、#define
、#undef
の各ディレクティブを組み合わせると、1 つ以上のシンボルが存在するかどうかに応じてコードを含めたり除外したりできます。 条件付きコンパイルは、デバッグ ビルドのコードをコンパイルする場合や、特定の構成用にコンパイルを行う場合に役立ちます。
#elif
を使用すると、複合条件付きディレクティブを作成できます。 前の #elif
または前の省略可能な #if
ディレクティブ式がいずれも #elif
と評価されない場合、true
式が評価されます。 #elif
式が true
と評価された場合は、#elif
と次の条件付きディレクティブの間にあるすべてのコードが、コンパイラによって評価されます。 たとえば:
#define VC7
//...
#if DEBUG
Console.WriteLine("Debug build");
#elif VC7
Console.WriteLine("Visual Studio 7");
#endif
#else
を使用すると、複合条件付きディレクティブを作成できるため、前の #if
または (省略可能な) #elif
ディレクティブの式がいずれも true
と評価されない場合は、コンパイラによって #else
と次の #endif
の間のすべてのコードが評価されます。 #endif
(#endif) が、#else
の後の次のプリプロセッサ ディレクティブになる必要があります。
#endif
は、#if
ディレクティブで始まる条件付きディレクティブの終了を示します。
次の例では、ファイルで MYTEST
シンボルを定義してから、MYTEST
および DEBUG
シンボルの値をテストする方法を示します。 この例の出力は、プロジェクトをデバッグとリリースのどちらの構成モードでビルドしたかによって異なります。
#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
}
}
次の例は、各種ターゲット フレームワークをテストし、可能な場合には新しい API を使用できるようにする方法を示しています。
public class MyClass
{
static void Main()
{
#if NET40
WebClient _client = new WebClient();
#else
HttpClient _client = new HttpClient();
#endif
}
//...
}
シンボルの定義
次の 2 つのプリプロセッサ ディレクティブを使用して、条件付きコンパイルのシンボルの定義またはその解除を行います。
-
#define
: シンボルを定義します。 -
#undef
: シンボルの定義を解除します。
#define
は、シンボルを定義するために使用します。 #if
ディレクティブに渡される式としてシンボルを使用すると、次の例に示すように、式は true
に評価されます。
#define VERBOSE
#if VERBOSE
Console.WriteLine("Verbose output version");
#endif
注意
C# では、const
キーワードを使用してプリミティブ定数を定義する必要があります。 const
宣言により、実行時に変更できない static
メンバーが作成されます。 #define
ディレクティブを使用して、通常 C および C++ で行うように定数値を宣言することはできません。 そのような定数がいくつかある場合は、それを保持するための "Constants" クラスを個別に作成することを検討してください。
シンボルを使用して、コンパイル条件を指定できます。 シンボルは、#if
または #elif
で評価できます。 また、ConditionalAttribute を使用して、条件付きコンパイルを実行することもできます。 シンボルを定義することはできますが、シンボルに値を代入することはできません。 #define
ディレクティブは、ファイル内で、プリプロセッサ ディレクティブではない他の命令よりも前に記述する必要があります。 シンボルは、DefineConstants コンパイラ オプションでも定義できます。 #undef
を使うと、シンボルを未定義状態にできます。
領域の定義
次の 2 つのプリプロセッサ ディレクティブを使用して、アウトラインで折りたたむことができるコードの領域を定義できます。
-
#region
: 領域を開始します。 -
#endregion
: 領域を終了します。
#region
を使用すると、コード ブロックを指定できます。このブロックは、コード エディターのアウトライン機能を使用して、展開や折りたたみを行うことができます。 コード ファイルが長い場合は、現在操作しているファイルの部分に集中できるように 1 つまたは複数の領域を折りたたむ (非表示にする) と便利です。 次の例では、領域を定義する方法を示します。
#region MyClass definition
public class MyClass
{
static void Main()
{
}
}
#endregion
#region
ブロックは、#endregion
ディレクティブで終了させる必要があります。 #region
ブロックは、#if
ブロックと重複することはできません。 しかし、#region
ブロックを #if
ブロック内に入れ子にしたり、#if
ブロックを #region
ブロック内に入れ子にしたりすることはできます。
エラーと警告の情報
次のディレクティブを使用して、ユーザー定義のコンパイラ エラーと警告を生成し、行情報を制御するようにコンパイラに指示します。
-
#error
: 指定されたメッセージでコンパイラ エラーを生成します。 -
#warning
: 特定のメッセージでコンパイラ警告を生成します。 -
#line
: コンパイラ メッセージで出力される行番号を変更します。
#error
を使用すると、コード内の特定の場所からユーザー定義の CS1029 エラーを生成できます。 たとえば:
#error Deprecated code in this method.
注意
コンパイラは #error version
を特別な方法で処理し、使用されているコンパイラと言語バージョンを含むメッセージと共にコンパイラ エラー CS8304 を報告します。
#warning
を使用すると、コード内の特定の場所から CS1030 レベル 1 のコンパイラの警告を生成できます。 たとえば:
#warning Deprecated code in this method.
#line
を使用すると、コンパイラの行番号および (必要に応じて) エラーと警告に出力されるファイル名を変更することができます。
次の例では、行番号に関連付けられている 2 つの警告を報告する方法を示します。 #line 200
ディレクティブは、次の行の番号を強制的に 200 にし (既定値は #6 ですが)、次の #line
ディレクティブまで、ファイル名は "Special" として報告されます。#line default
ディレクティブは、行番号を既定の段落番号に返します。これは、前のディレクティブで番号が付け直された行をカウントします。
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;
}
}
コンパイルで生成される出力は次のとおりです。
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
#line
ディレクティブは、ビルド プロセスで自動化された中間ステップで使用される場合があります。 たとえば、行が元のソース コード ファイルから削除されても、ファイル内の元の行番号付けに基づいてコンパイラに引き続き出力を生成させる場合は、行を削除してから #line
を使用して元の行番号付けをシミュレートできます。
開発者がコードをステップ実行すると、#line hidden
と次の #line
ディレクティブ (別の #line hidden
ディレクティブではないと仮定) の間のすべての行がステップ オーバーされるように、#line hidden
ディレクティブによって、デバッガーに対して連続する行が非表示にされます。 このオプションは、ユーザー定義のコードとコンピューターによって生成されたコードを ASP.NET が区別できるようするために使用することもできます。 ASP.NET はこの機能の主要なコンシューマーですが、それを利用するソース ジェネレーターが増える可能性があります。
#line hidden
ディレクティブは、エラー報告でのファイル名や行番号には影響しません。 つまり、コンパイラが非表示ブロックでエラーを検出した場合、コンパイラはエラーの現在のファイル名と行番号を報告します。
#line filename
ディレクティブは、コンパイラ出力に表示するファイル名を指定します。 既定では、ソース コード ファイルの実際の名前が使用されます。 ファイル名は二重引用符 ("") で、行番号に従う必要があります。
#line
ディレクティブの新しい形式を使用できます。
#line (1, 1) - (5, 60) 10 "partial-class.cs"
/*34567*/int b = 0;
この形式のコンポーネントは次のとおりです。
-
(1, 1)
: ディレクティブに続く行の最初の文字の開始行と列。 この例では、次の行は行 1、列 1 として報告されます。 -
(5, 60)
: マークされた領域の終了行と列。 -
10
:#line
ディレクティブが有効にする列オフセット。 この例では、10 番目の列が列 1 として報告されます。 宣言int b = 0;
は、その列から始まります。 このフィールドはオプションです。 省略した場合、ディレクティブは最初の列に対して有効になります。 -
"partial-class.cs"
: 出力ファイルの名前。
前の例では、次の警告が生成されます。
partial-class.cs(1,5,1,6): warning CS0219: The variable 'b' is assigned but its value is never used
再マップ後、変数 b
は、ファイル partial-class.cs
の最初の行の 6 文字目に表示されます。
ドメイン固有言語 (DSL) では通常、この形式を使用し、ソース ファイルから生成後の C# 出力へのマッピングを向上させます。 この拡張 #line
ディレクティブの最も一般的な用途は、生成されたファイルに表示される警告またはエラーを元のソースに再マップすることです。 たとえば、次の razor ページを考えてみます。
@page "/"
Time: @DateTime.NowAndThen
プロパティ DateTime.Now
が DateTime.NowAndThen
と誤って入力されました。 この Razor スニペットに対して生成された C# は、page.g.cs
で次のようになります。
_builder.Add("Time: ");
#line (2, 6) - (2, 27) 15 "page.razor"
_builder.Add(DateTime.NowAndThen);
上記のスニペットのコンパイラ出力は次のとおりです。
page.razor(2, 2, 2, 27)error CS0117: 'DateTime' does not contain a definition for 'NowAndThen'
page.razor
の 2 行目の 6 行目は、ディレクティブの @DateTime.NowAndThen
によって示されるテキスト (2, 6)
が開始される場所です。 この @DateTime.NowAndThen
のスパンは、ディレクティブの (2, 27)
によって示される 2 行目の列 27 で終了します。 DateTime.NowAndThen
のテキストは、ディレクティブの page.g.cs
によって示される、15
の列 15 から始まります。 コンパイラは、page.razor
の場所でエラーを報告します。 開発者は、生成されたソースではなく、ソース コード内のエラーに直接移動できます。
この形式のその他の例については、機能仕様の例に関するセクションを参照してください。
プラグマ
#pragma
は、ファイル内に指定され、そのファイルのコンパイルについての特別な命令をコンパイラに指示します。 コンパイラは、使用するプラグマをサポートする必要があります。 つまり、#pragma
を使用してカスタムの前処理命令を作成することはできません。
-
#pragma warning
: 警告を有効または無効にします。 -
#pragma checksum
: チェックサムを生成します。
#pragma pragma-name pragma-arguments
ここで、pragma-name
は認識されているプラグマの名前で、pragma-arguments
は pragma 固有の引数です。
#pragma warning
#pragma warning
を使用すると、特定の警告を有効または無効にすることができます。 #pragma warning disable format
と #pragma warning enable format
は、Visual Studio でコード ブロックを書式設定する方法を制御します。
#pragma warning disable warning-list
#pragma warning restore warning-list
ここで、warning-list
は、414, CS3021
などの警告番号のコンマ区切りのリストです。 "CS" というプレフィックスは省略可能です。 警告番号が指定されていないと、disable
はすべての警告を無効にし、restore
はすべての警告を有効にします。
注意
Visual Studio で警告番号を調べるには、プロジェクトをビルドし、[出力] ウィンドウで警告番号を探してください。
disable
は、ソース ファイルの次の行から有効になります。 警告は、restore
の後の行で復元されます。 ファイルに restore
が存在しない場合、警告は、同じコンパイルの以降のファイルの最初の行で既定の状態に復元されます。
// 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()
{
}
}
warning
プラグマの別の形式では、Visual Studio の書式設定コマンドがコード ブロック内で無効または復元されます。
#pragma warning disable format
#pragma warning restore format
Visual Studio 形式のコマンドでは、disable format
が有効なコード ブロック内のテキストは変更されません。
#pragma checksum
ASP.NET ページのデバッグに使用するソース ファイルのチェックサムを生成します。
#pragma checksum "filename" "{guid}" "checksum bytes"
ここで、"filename"
は変更または更新の監視を必要とするファイルの名前、"{guid}"
はハッシュ アルゴリズムのグローバル一意識別子 (GUID)、"checksum_bytes"
はチェックサムのバイト数を表す 16 進数の文字列です。 偶数の 16 進数である必要があります。 奇数の数値を指定すると、コンパイル時に警告が出力され、ディレクティブが無視されます。
Visual Studio デバッガーは、常に正しいソースを検出するために、チェックサムを使用します。 コンパイラはソース ファイルのチェックサムを計算し、プログラム データベース (PDB) ファイルに結果を出力します。 デバッガーは、その PDB ファイルを使用して、ソース ファイルについて計算したチェックサムと比較します。
このソリューションは ASP.NET プロジェクトには使用できません。計算されたチェックサムは、.aspx ファイルではなく、生成されたソース ファイルを対象としているためです。 この問題に対応するため、#pragma checksum
によって ASP.NET ページのチェックサムがサポートされています。
Visual C# で ASP.NET プロジェクトを作成すると、生成されるソース ファイルにソースの生成元である .aspx ファイルのチェックサムが含められます。 コンパイラは、この情報を PDB ファイルに書き込みます。
コンパイラによってファイルで #pragma checksum
ディレクティブが検出されない場合、チェックサムが計算され、PDB ファイルにその値が書き込まれます。
class TestClass
{
static int Main()
{
#pragma checksum "file.cs" "{406EA660-64CF-4C82-B6F0-42D48172A799}" "ab007f1d23d9" // New checksum
}
}
.NET