所以前几天我在我们的代码库中遇到了这个小宝石,我想试着看看编写它的人是不是很懒,或者知道我不知道的东西。
标准事件处理程序是这样写的(我会 -
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 类型转换为同一类型并进行一些优化?
答案 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个字节,所以它非常小。)