2013-08-25 3 views
1

Так что на днях я нашел эту маленькую жемчужину в нашей кодовой базе, и я хотел попытаться посмотреть, был ли человек, который написал ее, просто ленив или знал что-то, чего у меня нет.C# type-casting sender

Стандартный обработчик событий был написан, как это (я буду. -

private void OnSomeEvent(IVehicle sender, ISomeArgs args){ 

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

} 

Я видел ли это сразу подумал о многочисленных ун-бокс операций типа литья, которые напрасно предавались здесь я вновь -wrote это как SO-

private void OnSomeEvent(IVehicle sender, ISomeArgs args){ 


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

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


} 

Так сделал первый пример знает, что-то я не? знаю ли компилятор, который мы пытаемся ип ящик типа отлитого к тому же типу куче рази сделать некоторую оптимизацию?

+2

Там нет распаковка происходит здесь вообще ,Типы интерфейсов являются ссылочными типами. –

+0

Да, я думаю, ваше право, я имел в виду тип-литье, я думаю. – thebringking

ответ

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 байт, это очень незначительные.)

+0

Отметьте это как ответ. Очень тщательное и точное объяснение, которое я искал. Я могу видеть дополнительные «isint» звонки в IL, но, как вы сказали, нет такой большой разницы. – thebringking

+0

вы также должны рассмотреть ** maxstack **, для оптимизированного кода требуется только «2», а для другого - «8». Откуда вы знаете это: «Нет никаких дополнительных приемов?» Я не знаком с «IL». –

+0

@KingKing Да, есть дополнительные вызовы 'isinst', которые выполняются в некотором роде. Я уточнил свой вывод, чтобы быть более точным и тщательным. – Gjeltema

1

Вам не нужно is при использовании asas, потому что возвращает реальный объект нужного типа, если это литьевой к этому типу, в противном случае значение 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(); 
    } 
    }  
} 
+0

Еще лучше. Благодарю. Любая идея по удалению бокса в первом примере? Был ли я прав бросать один раз, а не постоянно проверять (obj как Obj)? – thebringking

+0

@thebringking '(obj as Obj)' - это особый вид 'Try Cast', я думаю, что компилятор сделает это дважды, если не будет' 'алгоритм компиляции '' более сложным. В любом случае вы должны пойти на кастинг только один раз, это также более читаемо. –

1

бокс и распаковка осуществляется только между типами значений и ссылочными типами.

От msdn:

Распаковка является явным преобразованием из объекта типа к типу значения или от типа интерфейса в тип значения, который реализует интерфейс .

Таким образом, в вашем коде нет распаковки. Существует только серия типоразмеров, которая не влияет на производительность заметно.

+0

Хорошо, да, это воспитывалось. Так что это была микро-оптимизация для удобочитаемости. Но я думаю, это вопрос мнения. Спасибо за ответ! – thebringking