Учитывая, что это очень естественный случай использования (если вы не знаете, что на самом деле делает as
),
if (x is Bar) {
Bar y = x as Bar;
something();
}
является фактически эквивалентным (то есть генерируемый компилятором CIL из приведенного выше кода будет эквивалентен):
Bar y = x as Bar;
if (y != null) {
y = x as Bar; //The conversion is done twice!
something();
}
EDIT:
Думаю, я не задал вопрос. Я бы никогда не написал второй фрагмент, поскольку он, конечно, лишний. Я утверждаю, что CIL, сгенерированный компилятором при компиляции первого фрагмента, эквивалентен второму фрагменту, который является избыточным. Вопросы: а) Это правильно? b) Если да, то почему is
реализовано так?
Это потому, что я нахожу первый фрагмент намного понятнее и красивее, чем на самом деле хорошо написанный
Bar y = x as Bar;
if (y != null) {
something();
}
ВЫВОД:
Оптимизация случая is
/as
не является ответственностью компилятора, а JIT's.
Кроме того, как и при нулевой проверке, он имеет меньше (и менее дорогостоящих) инструкций, чем обе альтернативы (is
и as
и is
и cast
).
Приложение:
CIL как для nullcheck (.NET 3.5):
L_0001: ldarg.1
L_0002: isinst string
L_0007: stloc.0
L_0008: ldloc.0
L_0009: ldnull
L_000a: ceq
L_000c: stloc.1
L_000d: ldloc.1
L_000e: brtrue.s L_0019
L_0011: ldarg.0
L_0019: ret
CIL for is and cast (.NET 3.5):
L_0001: ldarg.1
L_0002: isinst string
L_0007: ldnull
L_0008: cgt.un
L_000a: ldc.i4.0
L_000b: ceq
L_000d: stloc.1
L_000e: ldloc.1
L_000f: brtrue.s L_0021
L_0012: ldarg.1
L_0013: castclass string
L_0018: stloc.0
L_0019: ldarg.0
L_0021: ret
CIL for is и as (.NET 3.5):
L_0001: ldarg.1
L_0002: isinst string
L_0007: ldnull
L_0008: cgt.un
L_000a: ldc.i4.0
L_000b: ceq
L_000d: stloc.1
L_000e: ldloc.1
L_000f: brtrue.s L_0021
L_0012: ldarg.1
L_0013: isinst string
L_0018: stloc.0
L_0019: ldarg.0
L_0021: ret
Они были отредактированы для краткости (объявления методов, nops и вызовы на что-то() удалены).