目次

キーワード

ポイント

  • プリプロセス命令: コンパイラや統合開発環境に特別な指示を出すために使う構文。

    • シンボル定義・条件コンパイル: 「デバッグ時のみ」など、条件によってコンパイル結果を変える。

    • 警告・エラーの生成

    • ソースコードの領域わけ: ソースコードを領域わけすることで、コードの可読性向上。

概要

プリプロセス命令(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 をコンパイルする際に DEBUGQUIET という名前のシンボルを定義したければ以下のようにします。 (ちなみに、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 で開くと以下のような見た目になります。

Visual Studio のアウトライン機能の例(展開時)
Visual Studio のアウトライン機能の例(展開時)

ここで、#region 命令の左に出ている [-] ボタンをクリックすると、 #region 命令で領域分けしたコードが折り畳まれ、以下のような見た目に変わります。

Visual Studio のアウトライン機能の例(折り畳み時)
Visual Studio のアウトライン機能の例(折り畳み時)

プラグマ

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

更新履歴

ブログ