1

先日、私たちのコードベースでこの小さな宝石に出くわしました。それを書いた人が怠け者なのか、それとも私が知らないことを知っているのかを試してみたかったのです。

標準のイベント ハンドラーは次のように記述されています (私は -

private void OnSomeEvent(IVehicle sender, ISomeArgs args){

 if((sender is Car) && (sender as Car).numWheels == 4 && (sender as Car).hasGas) 
 {
     (sender as Car).drive();
 }

}

これは、ここで不必要に行われている多数のボックス化解除型キャスト操作をすぐに考えたものでした。こんな感じに書き直しました~

private void OnSomeEvent(IVehicle sender, ISomeArgs args){


 if (sender is Car){
  Car _car = sender as Car;

   if(_car.numWheels == 4 && _car.hasGas){
     _car.drive();
   }
 }     


}

最初の例は、私が知らないことを知っていましたか? コンパイラは 、同じ型への型キャストを何度もボックス化解除して最適化しようとしていることを認識していますか?

4

3 に答える 3

1

その型にキャスト可能な場合は目的の型の実際のオブジェクトを返すため、is使用する場合は必要ありません。それ以外の場合は値が返されます。それが最も最適化された方法であるかどうかを確認するだけです:asasnullnull

private void OnSomeEvent(IVehicle sender, ISomeArgs args){    
   Car _car = sender as Car;
   if(_car != null){
    if(_car.numWheels == 4 && _car.hasGas){
     _car.drive();
    }
   }     
}
于 2013-08-25T15:58:56.430 に答える
1

ボックス化とボックス化解除は、値型と参照型の間でのみ行われます。

msdnから:

ボックス化解除は、型オブジェクトから値型へ、またはインターフェイス型からインターフェイスを実装する値型への明示的な変換です。

したがって、コードにボックス化解除はありません。性能に目立った影響を与えない一連の型キャストのみがあります。

于 2013-08-25T16:17:39.153 に答える
1

あなたはすでに回答を受け入れていますが、私はすでに調査を行っているので、投稿することにします。

リリース ビルドを実行するときに出力 IL を確認すると、2 つのセットアップの間にほとんど違いはありません。

最初の「最適化されていない」呼び出しからの出力 IL は次のようになります。

// Code size       47 (0x2f)
.maxstack  8
IL_0000:  ldarg.0
IL_0001:  isinst     TestApp.Program/Car
IL_0006:  brfalse.s  IL_002e
IL_0008:  ldarg.0
IL_0009:  isinst     TestApp.Program/Car
IL_000e:  callvirt   instance int32 TestApp.Program/Car::get_numWheels()
IL_0013:  ldc.i4.4
IL_0014:  bne.un.s   IL_002e
IL_0016:  ldarg.0
IL_0017:  isinst     TestApp.Program/Car
IL_001c:  callvirt   instance bool TestApp.Program/Car::get_hasGas()
IL_0021:  brfalse.s  IL_002e
IL_0023:  ldarg.0
IL_0024:  isinst     TestApp.Program/Car
IL_0029:  callvirt   instance void TestApp.Program/Car::drive()
IL_002e:  ret

2 番目の「最適化された」呼び出しからの出力 IL は次のようになります。

// Code size       34 (0x22)
.maxstack  2
.locals init ([0] class TestApp.Program/Car c)
IL_0000:  ldarg.0
IL_0001:  isinst     TestApp.Program/Car
IL_0006:  stloc.0
IL_0007:  ldloc.0
IL_0008:  brfalse.s  IL_0021
IL_000a:  ldloc.0
IL_000b:  callvirt   instance int32 TestApp.Program/Car::get_numWheels()
IL_0010:  ldc.i4.4
IL_0011:  bne.un.s   IL_0021
IL_0013:  ldloc.0
IL_0014:  callvirt   instance bool TestApp.Program/Car::get_hasGas()
IL_0019:  brfalse.s  IL_0021
IL_001b:  ldloc.0
IL_001c:  callvirt   instance void TestApp.Program/Car::drive()
IL_0021:  ret

isinst「最適化されていない」呼び出しで行われている追加の呼び出しがあります。これは最適化して取り除くのに適していますが、関数の実行時間に実質的な影響を与えることはありません。

とはいえ、C# コードは読みやすいため、「最適化」を行います。これは、パフォーマンスのマイクロ最適化よりも重要です (コードをプロファイリングし、パフォーマンスのボトルネックを最適化する必要があると判断するまでは)。

(また、スタックが少し大きくなりますが、高速ですぐにクリーンアップされるスタックの増加であり、6 バイトしかないため、非常にマイナーです。)

于 2013-08-25T16:24:36.097 に答える