C# 类型转换发送程序

本文关键字:程序 类型转换 | 更新日期: 2023-09-27 18:28:53

所以前几天我在我们的代码库中遇到了这个小宝石,我想试着看看写它的人是否只是懒惰,或者知道一些我不知道的事情。

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

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();
   }
 }     

}

那么第一个例子知道我不知道的事情吗?编译器是否知道我们正在尝试多次类型转换为同一类型并进行一些优化?

C# 类型转换发送程序

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

在执行发布生成时检查输出 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 个字节,所以它非常小。

使用

as 时不需要is,因为如果所需类型的对象可转换为该类型,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();
    }
   }     
}

箱和取消装箱仅在值类型和引用类型之间完成。

从 msdn:

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

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