次の方法で共有


C# プリプロセッサ ディレクティブ

コンパイラに個別のプリプロセッサはありませんが、このセクションに示すディレクティブは、ある場合と同じように処理されます。 これらを使用すると、条件付きコンパイルに役立ちます。 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 NETFRAMEWORKNET481NET48NET472NET471NET47NET462NET461NET46NET452NET451NET45NET40NET35NET20 NET48_OR_GREATERNET472_OR_GREATERNET471_OR_GREATERNET47_OR_GREATERNET462_OR_GREATERNET461_OR_GREATERNET46_OR_GREATERNET452_OR_GREATERNET451_OR_GREATERNET45_OR_GREATERNET40_OR_GREATERNET35_OR_GREATERNET20_OR_GREATER
.NET Standard NETSTANDARDNETSTANDARD2_1NETSTANDARD2_0NETSTANDARD1_6NETSTANDARD1_5NETSTANDARD1_4NETSTANDARD1_3NETSTANDARD1_2NETSTANDARD1_1NETSTANDARD1_0 NETSTANDARD2_1_OR_GREATERNETSTANDARD2_0_OR_GREATERNETSTANDARD1_6_OR_GREATERNETSTANDARD1_5_OR_GREATERNETSTANDARD1_4_OR_GREATERNETSTANDARD1_3_OR_GREATERNETSTANDARD1_2_OR_GREATERNETSTANDARD1_1_OR_GREATERNETSTANDARD1_0_OR_GREATER
.NET 5+ (および .NET Core) NETNET9_0NET8_0NET7_0NET6_0NET5_0NETCOREAPPNETCOREAPP3_1NETCOREAPP3_0NETCOREAPP2_2NETCOREAPP2_1NETCOREAPP2_0NETCOREAPP1_1NETCOREAPP1_0 NET9_0_OR_GREATERNET8_0_OR_GREATERNET7_0_OR_GREATERNET6_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 ANDROIDBROWSERIOSMACCATALYSTMACOSTVOSWINDOWS
[OS][version] (たとえば、IOS15_1)、
[OS][version]_OR_GREATER (たとえば、IOS15_1_OR_GREATER)

注意

  • バージョンレスのシンボルは、ターゲットとするバージョンに関係なく定義されます。
  • バージョン固有のシンボルは、対象とするバージョンに対してのみ定義されます。
  • <framework>_OR_GREATER シンボルは、ターゲットとするバージョンとそれ以前のすべてのバージョンに対して定義されます。 たとえば、.NET Framework 2.0 をターゲットとする場合、NET20NET20_OR_GREATERNET11_OR_GREATERNET10_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

booltrueまたは 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.NowDateTime.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 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 が有効なコード ブロック内のテキストは変更されません。 Ctrl + K、Ctrl + Dなどの書式コマンドでは、これらのコード領域は変更されません。 このプラグマを使用すると、コードの視覚的なプレゼンテーションを細かく制御できます。

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