プリプロセッサ
プリプロセッサとは
C#のコード上にはプログラムの実際の動作を書き、それをコンパイルして実行ファイルに変換します。
このコンパイルの直前に行う特定の処理をプリプロセッサ(プリプロセス命令)といいます。
(pre=前、process=処理)
プリプロセッサはコンパイラに特殊な指示を出すもので、実際のプログラムの動作には直接関係しませんが、上手く利用すれば生産性を向上させることができます。
プリプロセッサ用のキーワードは「#」記号から始まります。
プリプロセッサを記述する行にはひとつの命令のみが記述でき、それ以外の一切のコードは記述できません。
#define TEST
//↑これがプリプロセッサ
//こういうのはダメ
//#define TEST2 #define TEST3
using System;
using System.Collections.Generic;
//...
プリプロセッサ一覧
C#で用意されているプリプロセッサの一覧です。
- #if
- #else
- #elif
- #endif
- #define
- #undef
- #warning
- #error
- #line
- #region
- #endregion
- #pragma
- #pragma warning
- #pragma checksum
- #nullable
(→null許容参照型)
ここではよく使用されるプリプロセッサを説明します。
シンボルの定義
シンボルというのは後述する#if
命令で使用するための、コンパイラ用の定数のようなものです。
ただし定数と言っても値はなく、シンボル名のみを持ちます。
シンボル定義は#define
命令を使用します。
#define TEST
using System;
using System.Collections.Generic;
//...
上の例では「TEST」という名前のシンボルを定義しています。
#define
命令はコードの先頭で定義する必要があります。
それ以外の場所で定義するとエラーとなります。
C言語やC++などでは、#define
はプログラム上で使用可能な定数を定義できますが、C#ではそのような機能はありません。
defineコンパイラオプション
#define
命令と同じことはコンパイラオプションでも可能です。
VisualStudioならプロジェクトのプロパティから設定できます。
上部メニューから「(プロジェクト名)のプロパティ」を開きます。
「ビルド」メニューを選択し、「条件付きコンパイル シンボル」欄にシンボル名を記述することで、#defineと同じ効果が得られます。
(「#」記号は必要ありません)
半角スペースで区切ることで複数同時に設定できます。
#define
命令をソースコード上に記述する場合はそのソースファイル内でのみ有効となりますが、 コンパイラオプションでの設定は全てのコード上で有効になります。
また、コンパイラオプションでの設定はビルド構成ごとに設定可能です。
VisualStudioでは特殊なシンボル名として「DEBUG」と「TRACE」があらかじめ用意されており、チェックボックスでオン/オフができます。
その他、プログラムの実行環境によっていくつかのシンボルが定義済みになっています。
詳細は以下を参照してください。
#if プリプロセッサ ディレクティブ - C# リファレンス | Microsoft Docs
#undef
#undef
命令は、#define
命令で定義したシンボルを未定義にします。
この命令もコードの先頭で定義する必要があります。
コンパイラオプションで設定したシンボルを、そのソースコード上で無効にすることができます。
//DEBUGシンボルを無効
#undef DEBUG
using System;
using System.Collections.Generic;
//...
条件付きコンパイル
条件付きコンパイルは、シンボル定義に従ってコンパイル対象となるコードを切り替えることができます。
条件付きコンパイルには#if
命令、#elif
命令、#else
命令、#endif
命令を使用します。
#define TEST
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
#if TEST
Console.WriteLine("#TEST is defined"); //この行のみが実行される
#elif TEST2
Console.WriteLine("#TEST is not defined");
Console.WriteLine("#TEST2 is defined");
#else
Console.WriteLine("#TEST is not defined");
Console.WriteLine("#TEST2 is not defined");
#endif
Console.ReadLine();
}
}
}
#TEST is defined
動作としてはif文と同じです。
(#elif
は「else if文」に相当します)
上記コードは「TEST」シンボルが定義されているため、16行目が実行されます。
#if命令が実行された場合は#elif
、#else
は実行されません。
(コンパイル対象から除外されます)
#elif
命令、#else
命令は省略可能です。
コード先頭の#define
命令を書き換えて、実行結果が切り替わることを確認してください。
条件付きコンパイルでは論理演算子とtrue/false、およびそれらとの比較のための等値演算子、グループ化演算子を使用できます。
#if DEBUG && TEST
//DEBUGとTESTが定義されている
#endif
#if DEBUG || TEST
//DEBUGかTESTが定義されている
#endif
#if !DEBUG
//DEBUGが定義されていない
#endif
#if (DEBUG || TEST) && TEST2
//DEBUGかTESTが定義され、TEST2が定義されている
#endif
#if true
//無条件に有効
#endif
#if DEBUG == true
//「#if DEBUG」と同等
#endif
条件付きコンパイルはデバッグビルドとリリースビルドの切り替えでよく使用されます。
VisualStudioの既定の設定では、デバッグビルド時には「DEBUG」というシンボルが定義されます。
これを利用して、プログラム開発時には#if DEBUG
命令で開発に役立つメッセージなどを出力することができます。
static void Main(string[] args)
{
var data = SomeMethod();
#if DEBUG
//デバッグビルド時のみ
//変数の中身を確認する
Console.WriteLine(data);
#endif
}
リリースビルドに切り替えることで実際に配布するプログラムには不要なメッセージを表示しないようにする、ということが簡単にできるようになります。
開発時のみに必要なクラスやメソッドを作った場合も、それら全体を#if
命令で囲ってしまうことでリリースするプログラム上から除外し、軽量化することができます。
コードの領域の設定
VisualStudioのコードエディタは、標準でクラスやメソッド、名前空間などの単位でコードを折りたたむことができます。
行数の多いメソッドなどは、普段は折りたたんでおくことでコードの見通しが良くなります。
#region
命令、#endregion
命令は、この折りたたむ範囲を自分で定義することができます。
static void Main(string[] args)
{
//#region命令
#region 変数宣言
int num = 1;
float real = 2.3f;
string str = "abc";
#endregion
Console.WriteLine(num + real + str);
Console.ReadLine();
}
#region
~#endregion
で挟まれた箇所が新たなコード領域となります。
#region
命令には名前を付けることができ、この名前は折りたたみ時に表示されます。
(省略も可能です)
#region
命令の使用はメソッド内には限られないので、例えば関係する複数のメソッドを全て囲っておくなどの使用方法も可能です。
static void Main(string[] args)
{
//
}
#region 関数群
static void MethodA()
{
//
}
static void MethodB()
{
//
}
static void MethodC()
{
//
}
#endregion