連載

C#入門

第19回 プリプロセッサとドキュメント

(株)ピーデー
川俣 晶
2002/01/11


ソースにシンボルの定義を埋め込む

 シンボルをめったに切り替えない場合は、ソースコード上にシンボルの定義を直接書き込んでしまうこともできる。以下はその例である。

 1: #define MY_COMPILE_SWITCH
 2: using System;
 3:
 4: namespace ConsoleApplication5
 5: {
 6:   class Class1
 7:   {
 8:     static void Main(string[] args)
 9:     {
10: #if MY_COMPILE_SWITCH
11:       Console.WriteLine("MY_COMPILE_SWITCH on");
12: #else
13:       Console.WriteLine("MY_COMPILE_SWITCH off");
14: #endif
15:     }
16:   }
17: }
シンボル定義をしたサンプル・プログラム3
「#define」により、ソース内でシンボルを定義することもできる。

 これを実行すると以下のようになる。

サンプル・プログラム3の実行結果
最初の行で定義したシンボルにマッチする部分がコンパイルされているのが分かる。

 「#define」は、指定されたシンボルを定義することをプリプロセッサに対して指示する。ここでは、MY_COMPILE_SWITCHというシンボルを定義せよ、と指示している。プロジェクトのプロパティで定義しても、ソース内で定義しても、シンボルの性質に変わりはない。しかし、プロジェクトのプロパティで定義すればすべてのソースコードに適用されるが、ソースコード上に記述した場合は、そのソースファイル内にしか作用しないので注意が必要である。

シンボルを解除する

 まれに、プロジェクトのプロパティで定義したシンボルを、ソースコード中で打ち消したい場合がある。例えば以下のように、“MY_COMPILE_SWITCH”というシンボルがプロジェクトのプロパティで定義されているとする。

[プロパティページ]ダイアログ
“MY_COMPILE_SWITCH”というシンボルをプロジェクトのプロパティで定義している。

 このとき、ソースコード内でこれを打ち消したい場合は以下のように記述する。

 1: #undef MY_COMPILE_SWITCH
 2: using System;
 3:
 4: namespace ConsoleApplication6
 5: {
 6:   class Class1
 7:   {
 8:     static void Main(string[] args)
 9:     {
10: #if MY_COMPILE_SWITCH
11:       Console.WriteLine("MY_COMPILE_SWITCH on");
12: #else
13:       Console.WriteLine("MY_COMPILE_SWITCH off");
14: #endif
15:     }
16:   }
17: }
シンボルを打ち消すサンプル・プログラム4
「#undef」により、すでに定義されたシンボルを打ち消すことができる。

 これを実行すると以下のようになる。

サンプル・プログラム4の実行結果
ダイアログで定義したシンボルにマッチしない部分がコンパイルされているのが分かる。

 ここで注目すべきは1行目の「#undef」である。これは#defineとは逆の効能をプリプロセッサに指示する。つまり、指定されたシンボルを定義されていない状態に戻してくれる。

#ifとifの違い

 ここまで読んで、ifステートメントがあれば、#ifはなくても困らないのではないか、と思った読者もいるだろう。そこで、#ifとifの機能の違いがはっきりと分かる例を示そう。

 1: using System;
 2:
 3: namespace ConsoleApplication7
 4: {
 5:   class Class1
 6:   {
 7:     static void Main(string[] args)
 8:     {
 9: #if DEBUG
10:       Console.WriteLine("Hello!");
11: #else
12:       This is a pen!
13: #endif
14:     }
15:   }
16: }
#ifとifの違いを示すサンプル・プログラム5
条件にマッチしない部分はコンパイルされないため、何を記述しても無視される。

 これはデバッグ・ビルドでのみビルドでき、実行すると以下のようになる。

サンプル・プログラム5の実行結果
シンボル“DEBUG”が定義されていないとコンパイル・エラーとなる。

 ここで注目していただきたいのは12行目である。もちろん、12行目は、C#としては完全な文法違反である。このような行があれば、コンパイラはエラーを発してしまう。だが、このソースコードは、デバッグ・ビルドを行う限り、エラーも出さないで正常に処理を終了する。これは、プリプロセッサが、コンパイルする前に処理されるという役割を持っているためだ。プリプロセッサは、内容の如何にかかわらず、#if-#else-#endifで指定された範囲を抽出してコンパイラ本体に渡す。そのため、選ばれなかった条件に該当する部分は、ただ単に無視され、どんな文法エラーが記述されていようと、それはなかったこととして扱われる。一方、ifステートメントの方は、条件が成立する場合も成立しない場合も、どちらもコンパイル処理されるので、常に条件が決まっている式を書いても、成立しない側の文法チェックが行われてしまう。

 そのことから、プリプロセッサならではの使い方というものもある。例えば、ある環境ではコンパイル・エラーになるが、別の環境ではコンパイルできるソースというものが存在する場合がある。これはコンパイラのバージョン違いや、ライブラリの内容の相違などによって発生するものだ。こういう場合は、プリプロセッサで条件分けするようにしておけば、コンパイル・エラーになる部分をコンパイラ本体に処理させない、ということもできる。


 INDEX
  第19回 プリプロセッサとドキュメント
    1.プリプロセッサとは何か
  2.ソースにシンボルの定義を埋め込む
    3.複雑な条件の指定
    4.自作のエラーや警告を発する
    5.XMLドキュメントの埋め込み
    6.利用できるさまざまな要素
 
「C#入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間