ポイント
-
プリプロセス命令: コンパイラや統合開発環境に特別な指示を出すために使う構文。
-
シンボル定義・条件コンパイル: 「デバッグ時のみ」など、条件によってコンパイル結果を変える。
-
警告・エラーの生成
-
ソースコードの領域わけ: ソースコードを領域わけすることで、コードの可読性向上。
-
概要
プリプロセス命令(preprocessor directive)というものを用いることで、 条件付きコンパイル、エラーや警告の通知、ソースコードの領域分けなどを行うことが出来ます。 (directive はコンパイラへの指示という感じのニュアンスです。 operation などの単語と区別するために、「擬似命令」と訳したり、 そのままカタカナ語でディレクティブと書くこともあります。) すなわち、プロプロセス命令とは、コンパイラ等の開発環境に対する指示を行うための命令のことです。
プリプロセスとは、文字通りに意味を取ると、 コンパイルの前に行う処理のことです。 (最近はそうでもないけど、少なくともプリプロセスという言葉ができた当時は) C言語やC++言語では本当に文字通り、 コンパイルの前に命令の解釈を行っていたのでこのように呼ばれていました。 C# ではプリプロセス命令の解釈をコンパイルと同時に行っているので、 厳密には“プリプロセス”とは呼べないのですが、 C言語やC++言語のプリプロセス命令と似たような働きをしているため、 やはりプリプロセス命令という呼称が使われています。
プリプロセス命令
C# のプリプロセス命令用のキーワードは、全て #
から始まっています。
C# のプリプロセス命令には以下のようなものがあります。
シンボル定義
-
#define
-
#undef
条件付きコンパイル
-
#if
-
#else
-
#elif
-
#endif
エラー、警告の報告
-
#warning
-
#error
-
#line
ソースコードの領域分け
-
#region
-
#endregion
プラグマ
Ver. 2.0
#pragma
シンボル定義
#define
命令を用いると、シンボルの定義を行うことが出来ます。
定義したシンボルは if
命令の条件付きコンパイル命令で使用することが出来ます。
(例えば、DEBUG
という名前のシンボルが定義されている場合のみコンパイルされる部分を作ることが出来ます。)
シンボルの定義は以下のようにして行います。
#define シンボル名
また、シンボルの定義は C# コンパイラの /define
オプションを用いても行うことが出来ます。
例えば、csc を用いて test.cs
をコンパイルする際に DEBUG
と QUIET
という名前のシンボルを定義したければ以下のようにします。
(ちなみに、DEBUG
はデバッグ用のコードを生成したいときに、
QUIET
はエラーメッセージを画面等に出力したくないときに使うことが多いシンボル名です。)
csc /define:DEBUG;QUIET test.cs
ちなみに、複数のソースコードに渡ってシンボル定義を有効にしたい場合には、この /define オプションを使います。 (C++ の #include に相当するものがない。)
一方、#undef
命令を用いると、
#define
命令で定義したシンボルを消すことが出来ます。
シンボルの削除は以下のようにして行います。
#undef シンボル名
#define
, #undef
命令はソースの先頭でのみ使用することが出来ます。
それ以外の場所にこれらの命令を記述するとコンパイルエラーになります。
条件付きコンパイル
条件付きコンパイル命令を用いることで、
あるシンボルが定義されているときのみコンパイルされる部分を作ることが出来ます。
例えば、#if
命令を使って、
DEBUG
という名前のシンボルが定義されているときだけコンパイルされる部分を作ることでデバッグ用のコードを埋め込んだりします。
条件付きコンパイル命令は以下のようにして用います。
#if 条件1
条件1成立時に実行する部分
#elif 条件2
条件2成立時に実行する部分
#elif 条件3
条件3成立時に実行する部分
.
.
.
#else
条件不成立時に実行する部分
#endif
このうちで、#elif
の部分と #else
の部分は別になくてもかまいません。
(ちなみに、elif は else if を省略した語です。)
条件として使えるのは、シンボル名と true/false、および、
それらを &&
や ||
などの論理演算子でつないだものです。
条件式にシンボル名を用いた場合、そのシンボルが定義されている場合にのみ真として評価されます。
また、シンボル名の前に否定演算子 !
をつけることで、
そのシンボルが定義されていない場合にのみ真として評価することも出来ます。
例えば、以下のコードは DEBUG
が定義されていてかつ QUIET
が定義されていない場合にのみ真として評価されます。
#if DEBUG && !QUIET
Console.Write("a = {0}, b = {0}", a, b); // デバッグ用に変数の値を画面に出力
#endif
サンプル
#define B
using System;
class PreProcessTest
{
static void Main()
{
#if A
Console.Write("A という名前のシンボルが定義されています。\n");
#elif B
Console.Write("B という名前のシンボルが定義されています。\n");
#endif
}
}
普通にコンパイルした場合
B という名前のシンボルが定義されています。
コンパイルオプションに /define:A
と指定してコンパイルした場合
A という名前のシンボルが定義されています。
エラー、警告の報告
#warning
命令を用いることでユーザー定義の警告メッセージを、
#error
命令を用いることでユーザー定義のエラーメッセージを表示することが出来ます。
#warning 警告メッセージ
#error エラーメッセージ
例えば、以下のようにして使用します。
#if A
#warning まだ準備できてないから A を define しないで欲しいな。
#if B
#error ごめん、A と B を同時に define されちゃうと困るの。
#endif
#endif
また、#line
命令を用いることで、警告やエラー報告用の行番号を変更できます。
#line 行番号もしくは 'default'
サンプル
using System;
class PreProcessTest
{
static void Main()
{
#warning 7行目
#line 200
#warning 200行目
#warning 201行目
#line default
#warning 14行目
}
}
上記のコードをコンパイルすると以下のような警告メッセージが表示されます。
c:\test\class1.cs(7,10): warning CS1030: #warning : '7行目' c:\test\class1.cs(14,10): warning CS1030: #warning : '14行目' c:\test\class1.cs(200,10): warning CS1030: #warning : '200行目' c:\test\class1.cs(201,10): warning CS1030: #warning : '201行目'
ソースコードの領域分け
#region
、#endregion
命令を用いることで、
コードを領域分けすることが出来ます。
#region 領域の名前
プログラムコード
#endregion
C# では、通常、1つのファイルに1つのクラスを記述するので、
クラスの規模が大きくなるにつれ、ソースファイルの可読性が悪くなってきます。
そんなとき、この #region
命令を用いて領域分けをすることで、
可読性の向上を図ります。
(例えば、関連性のあるメソッドを集めて region で区切る等。)
また、#region
命令で領域分けされたコードブロックは
Visual Studio のコードエディタのアウトライン機能
(メソッドやクラスなどの意味のある単位ごとに領域の折り畳み/展開が出来る機能)を使用して
折り畳むことが出来ます。
例えば、以下のようなコードを書いたとします。
using System;
class PreProcessTest
{
static void Main()
{
Console.Write("area = {0}", width * height * PI);
}
#region 定数宣言用領域
const double PI = 3.1415926535897932;
const int width = 640;
const int height = 480;
#endregion
}
これを Visual Studio で開くと以下のような見た目になります。
ここで、#region
命令の左に出ている [-]
ボタンをクリックすると、
#region
命令で領域分けしたコードが折り畳まれ、以下のような見た目に変わります。
プラグマ
Ver. 2.0
C# 2.0 から、#pragma
命令が追加されました。
C++ をご存知の方向けの説明をするなら、一言、
ほぼ、C++ の #pragma と同様の機能です。
プラグマ(pragma)という言葉は、 ギリシャ語で「行為」という意味で、 転じて「実用主義」という意味(哲学用語)だそうです。
C++ では、#pragma
命令を使って、
実行環境(ハードウェアや OS)に依存した細かい指示や、
C++ の言語仕様的に非標準な指示をコンパイラに与えることができました。
(標準仕様では「対応していない pragma があった場合無視しろ」と決められている。)
例えば、インライン展開の制御や、構造体のバイトアラインの制御ができます。
また、コンパイラに対して、警告メッセージを出さないように指示することもできました。
今の所、
C# の #pragma
命令には、
警告メッセージの抑制(#pragma warning
)と、
ソースファイルの改変確認のためのチェックサム生成機能(#pragma checksum
)があります。
using System;
class Program
{
[Obsolete]
static void Foo() {}
static void Main() {
// 612番の警告(Obsolete メソッドを使用)を出さないようにする。
#pragma warning disable 612
Foo();
// 612番の警告を出すように戻す。
#pragma warning restore 612
}
}