2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Conditionalメソッドに渡す引数は評価されない

Posted at

#ConditionalAttribute

ログ用関数などによく使うConditionalAttributeという属性クラスがあります。
これは、voidを返すメソッドにつけると、Defineされたシンボル(DEBUGとか)の状態によってそのメソッドの呼び出しを有効にしたり無効にしてくれる属性です。

1
static void Main(string[] args)
{
    Log();
    Console.ReadKey();
}

[Conditional("LOG")]
static void Log()
{
    Console.WriteLine("YukaMaki");
}

#実行時評価される引数

例えば上記のようにすると、LOGシンボルが定義されているときのみコンソールに文字列が表示されます。では次のようにするとどうなるでしょうか?

2
static void Main(string[] args)
{
    Log("Hello World!" + TooHeavy());
    Console.ReadKey();
}

[Conditional("LOG")]
static void Log(string msg)
{
    Console.WriteLine(msg);
}

// 処理に1秒ぐらいかかるとする
static string TooHeavy() => DateTime.Now.ToString();

よく見る光景です。もし、引数はちゃんと評価されているのであれば、本番ビルドでもLogを通るたびに無駄な処理と無駄な文字列のアロケーションがされていることになります。
果たして、引数は評価されているのでしょうか?

3
static void Main(string[] args)
{
    Log("Hello World!" + TooHeavy());
    Console.WriteLine(isCalledHeavy);
    Console.ReadKey();
}

[Conditional("LOG")]
static void Log(string msg)
{
    Console.WriteLine(msg);
}

static bool isCalledHeavy = false;

static string TooHeavy()
{
    isCalledHeavy = true;
    return DateTime.Now.ToString();
}

引数の生成に副作用を持たせてみました。評価されるとisCalledHeavyが立つので評価されたことがわかります。

output
False

結論、引数も評価されなくなりました。無駄な処理も生まれずに安心安心…というわけにはいかず、これは逆に危ないかもしれません。

4
// OK
HogeClass hoge = null;
bool isSuccess = TryParse(ref hoge);
Log(isSuccess);

// hogeはnullのままだ!!
HogeClass hoge = null;
Log(TryParse(ref hoge));

下のように書いてしまうと、LOGシンボルの有無で全然処理が変わってしまいます。この場合、この後、Null例外が出て「リリースビルドだけばぐってる~~!!😵😵😵」になってしまうでしょう…
この場合は必要な処理が欠ける方ですが、3のソースで、Logの行が長いからと言って、引数をローカル変数に分割した場合には無駄な処理が発生する原因になりえます。

おまけ

string msg = "Hello World!" + TooHeavy();
Log(msg + "_"); // 引数を処理させるためにちょいと加工

これをコンパイルしてみます。

IL_0000: ldstr "Hello World!"
IL_0005: call string Program::TooHeavy()
IL_000a: call string [System.Runtime]System.String::Concat(string, string)
IL_000f: pop
IL_0016: ret

しっかり呼ばれていることが確認できますね。
Conditionalメソッドは、行そのものがなかったことにされています。
TooHeaveyが完全に副作用をもたらさない場合、JITによって消える可能性はある気はします。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?