C#类型转换发件人

时间:2013-08-25 15:57:15

标签: c# .net compiler-optimization unboxing

所以前几天我在我们的代码库中遇到了这个小宝石,我想试着看看编写它的人是不是很懒,或者知道我不知道的东西。

标准事件处理程序是这样写的(我会 -

private void OnSomeEvent(IVehicle sender, ISomeArgs args){

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

}

我立刻想到了这里不必要地进行的众多 un-boxing 类型转换操作。我重新写了它 -

private void OnSomeEvent(IVehicle sender, ISomeArgs args){


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

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


}

第一个例子是否知道我不知道的事情?编译器是否知道我们试图将 un-box 类型转换为同一类型并进行一些优化?

3 个答案:

答案 0 :(得分:1)

使用is时不需要as,因为as会返回所需类型的实际对象(如果它可以转换为该类型),否则null值退回。只是检查它是null是否是最优化的方式:

private void OnSomeEvent(IVehicle sender, ISomeArgs args){    
   Car _car = sender as Car;
   if(_car != null){
    if(_car.numWheels == 4 && _car.hasGas){
     _car.drive();
    }
   }     
}

答案 1 :(得分:1)

仅在值类型和引用类型之间进行装箱和拆箱。

来自msdn

  

取消装箱是从类型对象到值的显式转换   键入或从接口类型到实现该类型的值类型   接口

因此代码中没有任何拆箱。只有一系列的类型铸造不会明显影响性能。

答案 2 :(得分:1)

你已经接受了答案,但我已经完成了看,所以我想我会发布它。

在进行发布版本时检查输出IL,两个设置之间的差别很小。

第一个“未优化”调用的输出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

来自第二个“优化”调用的输出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个字节,所以它非常小。)