Conditional属性
部屋を整理してたら、昔勉強したC#の本が出てきて、こんなのあったなぁと思い出したので遊んでみた。
#ifdef HOGE 〜 #endif みたいなのをまたちょっと違う方法で実現できる属性。条件付きメソッド。TraceやDebugあたりの仕組みを実現するために作ったんだろうけど、誰でも使えたり。
#define A #define B using System; using System.Diagnostics; namespace ConditionalTest { class Program { static void Main( string[] args ) { if ( args.Length == 1 ) ConA(); ConB(); Console.Read(); } [Conditional( "A" )] private static void ConA() { Console.WriteLine( "A" ); } [Conditional( "B" )] private static void ConB() { Console.WriteLine( "B" ); } } }
先頭のdefineを消すだけで、ConAやConBのコールがIL上では呼ぶところごと消えてなくなります!ソース上は呼んでるように見えるので、たまたまコードを見たチームメイトが悲鳴をあげたりとかあるかもw
ソース上のコールは残って、ILだけで消えるってのは精神衛生上よろしくないかもしれません。手間いらずな反面、まぎらわしい〜。
このサンプルのようにifの中だったりするとどうよ?っていじめてみたけど、正しく生成されました。やるねぇ。でも、こういう使い方はするべきじゃないです、はい。
#define A, #define B のときのデバッグコンパイル結果のIL
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // コード サイズ 33 (0x21) .maxstack 2 .locals init ([0] bool CS$4$0000) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldlen IL_0003: conv.i4 IL_0004: ldc.i4.1 IL_0005: ceq IL_0007: ldc.i4.0 IL_0008: ceq IL_000a: stloc.0 IL_000b: ldloc.0 IL_000c: brtrue.s IL_0014 IL_000e: call void ConditionalTest.Program::ConA() IL_0013: nop IL_0014: call void ConditionalTest.Program::ConB() IL_0019: nop IL_001a: call int32 [mscorlib]System.Console::Read() IL_001f: pop IL_0020: ret } // end of method Program::Main
#define B だけのときのデバッグコンパイル結果のIL
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // コード サイズ 27 (0x1b) .maxstack 2 .locals init ([0] bool CS$4$0000) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldlen IL_0003: conv.i4 IL_0004: ldc.i4.1 IL_0005: ceq IL_0007: ldc.i4.0 IL_0008: ceq IL_000a: stloc.0 IL_000b: ldloc.0 IL_000c: brtrue.s IL_000e IL_000e: call void ConditionalTest.Program::ConB() IL_0013: nop IL_0014: call int32 [mscorlib]System.Console::Read() IL_0019: pop IL_001a: ret } // end of method Program::Main
call ConA() が消えてる。
#ちょっとコード変更しました。最初のは正しくいじめてなかった。