ConditionalAttribute
ログ用関数などによく使うConditionalAttribute
という属性クラスがあります。
これは、voidを返すメソッドにつけると、Defineされたシンボル(DEBUG
とか)の状態によってそのメソッドの呼び出しを有効にしたり無効にしてくれる属性です。
1
staticvoidMain(string[]args){Log();Console.ReadKey();}[Conditional("LOG")]staticvoidLog(){Console.WriteLine("YukaMaki");}
実行時評価される引数
例えば上記のようにすると、LOG
シンボルが定義されているときのみコンソールに文字列が表示されます。では次のようにするとどうなるでしょうか?
2
staticvoidMain(string[]args){Log("Hello World!"+TooHeavy());Console.ReadKey();}[Conditional("LOG")]staticvoidLog(stringmsg){Console.WriteLine(msg);}// 処理に1秒ぐらいかかるとするstaticstringTooHeavy()=>DateTime.Now.ToString();
よく見る光景です。もし、引数はちゃんと評価されているのであれば、本番ビルドでもLog
を通るたびに無駄な処理と無駄な文字列のアロケーションがされていることになります。
果たして、引数は評価されているのでしょうか?
3
staticvoidMain(string[]args){Log("Hello World!"+TooHeavy());Console.WriteLine(isCalledHeavy);Console.ReadKey();}[Conditional("LOG")]staticvoidLog(stringmsg){Console.WriteLine(msg);}staticboolisCalledHeavy=false;staticstringTooHeavy(){isCalledHeavy=true;returnDateTime.Now.ToString();}
引数の生成に副作用を持たせてみました。評価されるとisCalledHeavy
が立つので評価されたことがわかります。
output
False
結論、引数も評価されなくなりました。無駄な処理も生まれずに安心安心…というわけにはいかず、これは逆に危ないかもしれません。
4
// OKHogeClasshoge=null;boolisSuccess=TryParse(refhoge);Log(isSuccess);// hogeはnullのままだ!!HogeClasshoge=null;Log(TryParse(refhoge));
下のように書いてしまうと、LOG
シンボルの有無で全然処理が変わってしまいます。この場合、この後、Null例外が出て「リリースビルドだけばぐってる~~!!😵😵😵」になってしまうでしょう…
この場合は必要な処理が欠ける方ですが、3のソースで、Logの行が長いからと言って、引数をローカル変数に分割した場合には無駄な処理が発生する原因になりえます。
おまけ
stringmsg="Hello World!"+TooHeavy();Log(msg+"_");// 引数を処理させるためにちょいと加工
これをコンパイルしてみます。
IL_0000:ldstr"Hello World!"IL_0005:callstringProgram::TooHeavy()IL_000a:callstring[System.Runtime]System.String::Concat(string,string)IL_000f:popIL_0016:ret
しっかり呼ばれていることが確認できますね。
Conditionalメソッドは、行そのものがなかったことにされています。TooHeavey
が完全に副作用をもたらさない場合、JITによって消える可能性はある気はします。