C# プリプロセッサ ディレクティブ
コンパイラに個別のプリプロセッサはありませんが、このセクションに示すディレクティブは、ある場合と同じように処理されます。 これらを使用すると、条件付きコンパイルに役立ちます。 C や C++ のディレクティブとは異なり、マクロを作成するのにこれらのディレクティブを使用することはできません。 プリプロセッサ ディレクティブは、行の唯一の命令である必要があります。
Null 許容コンテキスト
#nullable
プリプロセッサ ディレクティブは、"Null 許容注釈コンテキスト" と "Null 許容警告コンテキスト" を設定します。 このディレクティブでは、Null 許容注釈の効果があるかどうか、および NULL 値の許容の警告が与えられるかどうかが制御されます。 各コンテキストは "無効" または "有効" になります。
どちらのコンテキストもプロジェクト レベル (C# ソース コードの外部) で指定することができ、Nullable
要素が PropertyGroup
要素に追加されます。 #nullable
ディレクティブは、注釈と警告のコンテキストを制御し、プロジェクト レベルの設定よりも優先されます。 ディレクティブは、別のディレクティブがそれをオーバーライドするか、ソース ファイルの末尾に達するまで、制御するコンテキストを設定します。
ディレクティブの効果は次のとおりです。
#nullable disable
: Null 許容注釈および警告コンテキストを "無効" に設定します。#nullable enable
: Null 許容注釈および警告コンテキストを "有効" に設定します。#nullable restore
: Null 許容注釈および警告コンテキストを、プロジェクト設定に復元します。#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
: 前の条件付きコンパイルを閉じます。
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)
の意味は同じです。 &&
(かつ)、||
(または)、!
(否定) の各演算子を使用すると、複数のシンボルが定義されているかどうかを評価することができます。 シンボルと演算子は、かっこを使用してグループ化できます。
以下は、下位互換性を維持しながら、コードで新しい .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 つ以上のシンボルが存在するかどうかに応じてコードを含めたり除外したりできます。 条件付きコンパイルは、デバッグ ビルドのコードをコンパイルする場合や、特定の構成用にコンパイルを行う場合に役立ちます。
#if
ディレクティブで始まる条件付きディレクティブは、#endif
ディレクティブで明示的に終了させる必要があります。 #define
を使用するとシンボルを定義できます。 シンボルを #if
ディレクティブに渡す式として使用すると、この式は true
と評価されます。 シンボルは、DefineConstants コンパイラ オプションでも定義できます。 #undef
を使うと、シンボルを未定義状態にできます。 #define
を使用して作成したシンボルのスコープは、そのシンボルが定義されているファイルです。 DefineConstants または #define
で定義したシンボルは、同じ名前の変数とは競合しません。 つまり、変数名をプリプロセッサ ディレクティブに渡すことはできません。シンボルはプリプロセッサ ディレクティブによってのみ評価できます。
#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
ディレクティブで始まる条件付きディレクティブの終了を示します。
ビルド システムは、SDK 型プロジェクトの各種ターゲット フレームワークを表す、定義済みプリプロセッサ シンボルも認識します。 これは、複数の .NET バージョンをターゲットとできるアプリケーションを作成する場合に役立ちます。
ターゲット フレームワーク | Symbols | 追加のシンボル (.NET 5 以降の SDK で使用可能) |
プラットフォーム シンボル (OS 固有の TFM を指定する場合のみ使用可能) |
---|---|---|---|
.NET Framework | |||
.NET Standard | |||
.NET 5+ (および .NET Core) | [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 シンボルは、ビルド構成プロパティ ("デバッグ" モードまたは "リリース" モード) に応じて自動的に設定されます。
次の例では、ファイルで 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
Note
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
ディレクティブは、コンパイラ出力に表示するファイル名を指定します。 既定では、ソース コード ファイルの実際の名前が使用されます。 ファイル名は、二重引用符 ("") で囲み、前に行番号を付ける必要があります。
C# 10 より、新しい形式の #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
でテキスト @DateTime.NowAndThen
が開始されるのは 2 行目の列 6 です。 これは、ディレクティブで (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 警告
#pragma warning
を使用すると、特定の警告を有効または無効にすることができます。
#pragma warning disable warning-list
#pragma warning restore warning-list
ここで、warning-list
は警告番号のコンマ区切りのリストです。 "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()
{
}
}
#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