C#プリプロセッサディレクティブ:条件付きコンパイルとコード制御
プリプロセッサディレクティブは、実際のコンパイルが始まる前に情報を前処理するようコンパイラに指示を与えます。
すべてのプリプロセッサディレクティブは#で始まり、1行にプリプロセッサディレクティブのみを記述できます(前には空白のみ可)。プリプロセッサディレクティブはステートメントではないため、セミコロン(;)で終了しません。
C#コンパイラには独立したプリプロセッサはありませんが、あたかもあるかのようにディレクティブが処理されます。C#ではプリプロセッサディレクティブは条件付きコンパイルの補助に使用されます。C/C++のディレクティブとは異なり、マクロの作成には使用されません。
C#のプリプロセッサディレクティブ一覧
| No. | ディレクティブと説明 |
|---|---|
| 1 | #define - シンボルと呼ばれる文字列を定義 |
| 2 | #undef - シンボルを未定義にする |
| 3 | #if - シンボルをテストしてtrueかどうか判定 |
| 4 | #else - #ifと組み合わせて複合条件ディレクティブを作成 |
| 5 | #elif - 複合条件ディレクティブを作成 |
| 6 | #endif - 条件付きディレクティブの終了を指定 |
| 7 | #line - コンパイラの行番号と(オプションで)ファイル名出力を変更 |
| 8 | #error - コードの特定位置からエラーを生成 |
| 9 | #warning - コードの特定位置からレベル1警告を生成 |
| 10 | #region - Visual Studioのアウトライン機能で展開/折りたたみ可能なコードブロックを指定 |
| 11 | #endregion - #regionブロックの終了をマーク |
#defineプリプロセッサ
#defineプリプロセッサディレクティブはシンボリック定数を作成します。
#defineを使用すると、シンボルを定義し、そのシンボルを#ifディレクティブに渡す式として使用することで、式がtrueと評価されるようにできます。
#define SYMBOL
#defineの例
#define PI
using System;
namespace PreprocessorDAppl {
class Program {
static void Main(string[] args) {
#if (PI)
Console.WriteLine("PI is defined");
#else
Console.WriteLine("PI is not defined");
#endif
Console.ReadKey();
}
}
}
実行結果:
PI is defined
条件付きディレクティブ
#ifディレクティブを使用して条件付きディレクティブを作成できます。シンボルがtrueと評価されるかテストするのに便利です。trueと評価される場合、コンパイラは#ifと次のディレクティブの間のすべてのコードを評価します。
構文
#if symbol [operator symbol]...
ここで、symbolはテストしたいシンボルの名前です。trueとfalseを使用したり、否定演算子をシンボルに前置したりできます。
operator symbolはシンボルの評価に使用される演算子です。以下のいずれかです。
==(等価)!=(不等価)&&(論理AND)||(論理OR)
シンボルと演算子を括弧でグループ化できます。条件付きディレクティブは、デバッグビルド用のコードコンパイルや特定の構成でのコンパイルに使用されます。#ifで始まる条件付きディレクティブは、明示的に#endifで終了する必要があります。
条件付きディレクティブの例
#define DEBUG
#define VC_V10
using System;
public class TestClass {
public static void Main() {
#if (DEBUG && !VC_V10)
Console.WriteLine("DEBUG is defined");
#elif (!DEBUG && VC_V10)
Console.WriteLine("VC_V10 is defined");
#elif (DEBUG && VC_V10)
Console.WriteLine("DEBUG and VC_V10 are defined");
#else
Console.WriteLine("DEBUG and VC_V10 are not defined");
#endif
Console.ReadKey();
}
}
実行結果:
DEBUG and VC_V10 are defined
#region / #endregionによるコード領域管理
Visual Studioなどのエディタで、コードブロックを展開/折りたたみ可能にします。コンパイルには影響しません。
#region Public Methods
public void Method1() { }
public void Method2() { }
#endregion
#region Private Helpers
private void Helper1() { }
private void Helper2() { }
#endregion
#error / #warningによるコンパイル時通知
#if !NET6_0_OR_GREATER
#error This library requires .NET 6 or later.
#endif
#if DEBUG
#warning Debug build detected. Performance may be reduced.
#endif
#lineディレクティブ
デバッガやスタックトレースで表示される行番号とファイル名を変更します。コード生成ツールなどで有用です。
#line 100 "GeneratedCode.cs"
int x = 1; // この行は100行目として扱われる
#line default // デフォルトに戻す
プロジェクトファイル(.csproj)でのシンボル定義
ソースコード内の#define以外に、プロジェクトファイルでビルド構成ごとにシンボルを定義するのが一般的です。
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
よく使用される定義済みシンボル
| シンボル | 説明 |
|---|---|
DEBUG | デバッグビルド時に定義(通常) |
TRACE | トレースビルド時に定義(通常) |
NETFRAMEWORK | .NET Frameworkターゲット時 |
NETCOREAPP | .NET Coreターゲット時 |
NETSTANDARD | .NET Standardターゲット時 |
NET6_0_OR_GREATER | .NET 6.0以上ターゲット時 |
ベストプラクティス
- ソースコード内の
#defineは最小限にし、プロジェクト設定で管理する - 条件付きコンパイルはデバッグ/ログ/診断コード、プラットフォーム固有コード、機能フラグに限定する
- ビジネスロジックの分岐に条件付きコンパイルを使用しない(実行時の設定/戦略パターンを使用)
#regionは大規模なファイルの整理に使用し、クラス/メソッド単位での分割を優先する#errorでビルド要件(ターゲットフレームワーク、依存関係)を強制する- シンボル名は大文字スネークケース(
MY_FEATURE)で統一する
C言語
- C# Hello World – 初めてのC#アプリケーション構築
- C# キーワードと識別子:ルール、一覧、ベストプラクティス完全ガイド
- C# 変数とプリミティブデータ型:完全マスターガイド
- C#演算子完全ガイド:C#における演算子の包括的解説
- C# ビット演算子とビットシフト演算子をマスターする:包括的ガイド
- C# 入出力の基礎:ユーザー入力と出力の基本をマスター
- C# の式、文、ブロック:実践例で徹底理解
- C# コメントをマスターする:種類、ベストプラクティス、XML ドキュメント
- C# switch 文をマスターする:構文、例、ベストプラクティス
- C# 三項演算子:条件式を簡潔に書くためのガイド
- C#プリプロセッサディレクティブ完全ガイド:条件付きコンパイルと診断制御