条件付きコンパイルや、VS.NETでの表示領域の制御などを処理するプリプロセッサ。使わなくてもプログラムは組めるが、開発現場では必須の機能だ。
本記事は、(株)技術評論社が発行する書籍『新プログラミング環境 C#がわかる+使える』から許可を得て転載したものです。同書籍に関する詳しい情報については、本記事の最後に掲載しています。
プリプロセッサは、ソース・コードをコンパイルする前に、前処理を行う機能である。これにより、ソース・コードでは表現しがたい多くの便利な機能が実現されている。例えば、テストやデバッグ時には便利な、条件付きコンパイルなどが利用できる。使わなくてもプログラムは組めるが、開発現場では必須の機能といえるだろう。
プリプロセッサはコンパイルの前に事前に処理を行う機能であるため、厳密にいうと、この機能はプログラミング言語の機能の一部ではない。しかし、通常は両者が一体となって処理されるので、プログラミング言語の一部と考えても問題はない。それでは、さっそくプリプロセッサが使われる例をList 19-1で見てみよう。
1: using System;
2:
3: namespace Sample001
4: {
5: class Class1
6: {
7: [STAThread]
8: static void Main(string[] args)
9: {
10: #if DEBUG
11: Console.WriteLine("DEBUG build");
12: #else
13: Console.WriteLine("RELASE build");
14: #endif
15: }
16: }
17: }
これを実行するとFig.19-1のようになる。
Fig.19-1を見て分かるとおり、このサンプル・ソースは、デバッグ・ビルドとリリース・ビルドでは、同じ実行結果にならない。普通なら、デバッグ・ビルドとリリース・ビルドで結果が違うというのはコンパイラの欠陥を意味するのだが、これは違う。このサンプル・ソースは、明示的に、ビルドの方法に応じて結果が異なるように指示を記述しているからだ。このように、ビルドの方法に応じて処理内容を一部変更したい、というニーズはしばしば発生する。例えば、デバッグ情報の出力はデバッグ・ビルドの場合に限りたい、といった事情があるときだ。
10、12、14行目の#if、#else、#endifは、プリプロセッサへの命令である。#ifに続いて記述された条件が成立する場合は、#ifと#elseの間がコンパイルされ、そうでないときは#elseと#endifの間がコンパイルされる。ここで条件として指定されているものは、10行目の#ifに続いて記述されているDEBUGである。DEBUGとは、デバッグ・ビルドの際に自動的に定義されるシンボルである。単にシンボル名を記述すると、そのシンボルが定義されているかどうかが判定される。その結果、10行目の#ifは、DEBUGというシンボルが定義されているかどうかを判定することになる。それは結果的にデバッグ・ビルドかどうかを判定することになる。
ちなみに、リリース・ビルドのときに定義されるシンボルというのはデフォルト状態では存在しないので、「#if RELEASE」というように記述しても機能しない。
デバッグ・ビルドとリリース・ビルドを区別するシンボルDEBUGは、Visual Studio .NETのIDEが自動的に用意してくれるので、何の準備もなく利用できる。しかし、自分でシンボルを用意することもできる。例えば、製品版と体験版を切り替えるためのシンボルを用意する、ということができる。
シンボルDEBUGの定義は、プロジェクトのプロパティを開くと、Fig.19-2のように[条件付きコンパイル定数]として見ることができる。
自分で追加のシンボルを用意したいときは、Fig.19-3のようにここにセミコロン(;)で区切って続けて入力すればよい(IDEを使わないときはコンパイラのコマンドライン引数で指定する)。
こうして定義したシンボルは、もちろん、ソース・コード中の#ifで参照できる。List 19-2は参照してみたサンプル・ソースである。
1: using System;
2:
3: namespace Sample002
4: {
5: class Class1
6: {
7: [STAThread]
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: }
これを実行するとFig.19-4のようになる。
なお、シンボルが定義されていない状態にするには、プロジェクトのプロパティにシンボルを書かなければよい。
シンボルをめったに切り替えない場合は、ソース・コード上にシンボルの定義を直接書き込んでしまうこともできる。List 19-3はその例である。
1: #define MY_COMPILE_SWITCH
2: using System;
3:
4: namespace Sample003
5: {
6: class Class1
7: {
8: [STAThread]
9: static void Main(string[] args)
10: {
11: #if MY_COMPILE_SWITCH
12: Console.WriteLine("MY_COMPILE_SWITCH on");
13: #else
14: Console.WriteLine("MY_COMPILE_SWITCH off");
15: #endif
16: }
17: }
18: }
これを実行するとFig.19-5のようになる。
#defineは、プリプロセッサに、指定されたシンボルを定義することを指示する。ここでは、MY_COMPILE_SWITCHというシンボルを定義せよと指示している。プロジェクトのプロパティで定義してもソース内で定義しても、シンボルの性質に変わりはない。しかし、プロジェクトのプロパティで定義すればすべてのソース・コードに適用されるが、ソース・コード上に記述した場合は、そのソース・ファイル内にしか作用しないことに注意する必要がある。つまりソース・ファイルa.csとb.csからなるプロジェクトで、a.csでシンボルを定義しても、それはb.csには影響を及ぼさないということである。
Copyright© Digital Advantage Corp. All Rights Reserved.