Почему оператор is вызывает ненужный бокс?
Документация сопоставления константных шаблонов с состояниями is
-operator (expr is constant
):
Постоянное выражение оценивается следующим образом:
-
Если expr
и constant
являются целочисленными типами, оператор равенства С# определяет, возвращает ли выражение значение true
(то есть является ли expr == constant
).
-
В противном случае значение выражения определяется путем вызова статического метода Object.Equals(expr, constant)
.
Поэтому при использовании этого кода
public bool IsZero(int value)
{
return value is 0;
}
Я ожидаю, что он будет использовать оператор ==
(случай 1) и сгенерировать этот код:
.method public hidebysig instance bool
IsZero(
int32 'value'
) cil managed
{
.maxstack 8
ldarg.1
ldc.i4.0
ceq
ret
}
Однако в действительности целочисленный параметр и константа (литерал) заключены в Object.Equals
для передачи в статический метод Object.Equals
(случай 2):
.method public hidebysig instance bool
IsZero(
int32 'value'
) cil managed
{
.maxstack 8
ldc.i4.0
box [mscorlib]System.Int32
ldarg.1
box [mscorlib]System.Int32
call bool [mscorlib]System.Object::Equals(object, object)
ret
}
Почему это так?
Ответы
Ответ 1
Компилятор одинаков во всех случаях - Roslyn. Разные версии производят разные IL, хотя. Версии С# 8 не коробятся, в то время как старые делают.
Например, с 2.9.0 IL для этого фрагмента:
using System;
public class C {
public bool IsZero(int value)
{
return value is 0;
}
}
является
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: box [mscorlib]System.Int32
IL_0007: ldarg.1
IL_0008: box [mscorlib]System.Int32
IL_000d: call bool [mscorlib]System.Object::Equals(object, object)
IL_0012: stloc.0
IL_0013: br.s IL_0015
IL_0015: ldloc.0
IL_0016: ret
Использование любой из версий С# 8, хотя производит это в режиме отладки:
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldc.i4.0
IL_0003: ceq
IL_0005: stloc.0
IL_0006: br.s IL_0008
IL_0008: ldloc.0
IL_0009: ret
и это в выпуске.
IL_0000: ldarg.1
IL_0001: ldc.i4.0
IL_0002: ceq
IL_0004: ret
Это так же, как ожидаемый код в вопросе
Ответ 2
Документация оператора гласит:
При выполнении по шаблону с постоянным рисунком, is
тесты, равняется ли выражение с заданным постоянным. В C# 6
и более ранних версиях шаблон констант поддерживается оператором switch
. Начиная с C# 7.0
, он также поддерживается оператором is
.
По умолчанию VS2017 использует более старую версию C#
компилятора. Вы можете включить функции C# 7.0
, установив Microsoft.Net.Compilers
from NuGet, который можно использовать для компиляции кода с последней версией компилятора.