ソースにシンボルの定義を埋め込む
シンボルをめったに切り替えない場合は、ソースコード上にシンボルの定義を直接書き込んでしまうこともできる。以下はその例である。
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ステートメントの方は、条件が成立する場合も成立しない場合も、どちらもコンパイル処理されるので、常に条件が決まっている式を書いても、成立しない側の文法チェックが行われてしまう。
そのことから、プリプロセッサならではの使い方というものもある。例えば、ある環境ではコンパイル・エラーになるが、別の環境ではコンパイルできるソースというものが存在する場合がある。これはコンパイラのバージョン違いや、ライブラリの内容の相違などによって発生するものだ。こういう場合は、プリプロセッサで条件分けするようにしておけば、コンパイル・エラーになる部分をコンパイラ本体に処理させない、ということもできる。
Insider.NET 記事ランキング
本日
月間