为什么 is-operator 会导致不必要的装箱?
Why does the is-operator cause unnecessary boxing?
常量模式与 is
运算符 (expr is constant
) 匹配的 documentation 指出:
The constant expression is evaluated as follows:
If expr
and constant
are integral types, the C# equality operator determines whether the expression returns true
(that is, whether expr == constant
).
Otherwise, the value of the expression is determined by a call to the static Object.Equals(expr, constant)
method.
因此,当使用此代码时
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
}
然而,在 reality 中,整数参数和常量(文字)被装箱以便传递给静态 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
}
为什么会这样?
编译器在所有情况下都是相同的 - Roslyn。虽然不同的版本产生不同的 IL。 C# 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
使用任何 C# 8 versions 虽然在调试模式下会产生这个:
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
这在 Release 中。
IL_0000: ldarg.1
IL_0001: ldc.i4.0
IL_0002: ceq
IL_0004: ret
这与问题中的预期代码相同
When performing pattern matching with the constant pattern, is
tests
whether an expression equals a specified constant. In C# 6
and earlier
versions, the constant pattern is supported by the switch
statement.
Starting with C# 7.0
, it's supported by the is
statement as well.
默认 VS2017 使用旧版本 C#
编译器。您可以通过从 NuGet 安装 Microsoft.Net.Compilers
来启用 C# 7.0
功能,这可用于使用最新版本的编译器编译代码。
常量模式与 is
运算符 (expr is constant
) 匹配的 documentation 指出:
The constant expression is evaluated as follows:
If
expr
andconstant
are integral types, the C# equality operator determines whether the expression returnstrue
(that is, whetherexpr == constant
).Otherwise, the value of the expression is determined by a call to the static
Object.Equals(expr, constant)
method.
因此,当使用此代码时
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
}
然而,在 reality 中,整数参数和常量(文字)被装箱以便传递给静态 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
}
为什么会这样?
编译器在所有情况下都是相同的 - Roslyn。虽然不同的版本产生不同的 IL。 C# 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
使用任何 C# 8 versions 虽然在调试模式下会产生这个:
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
这在 Release 中。
IL_0000: ldarg.1
IL_0001: ldc.i4.0
IL_0002: ceq
IL_0004: ret
这与问题中的预期代码相同
When performing pattern matching with the constant pattern,
is
tests whether an expression equals a specified constant. InC# 6
and earlier versions, the constant pattern is supported by theswitch
statement. Starting withC# 7.0
, it's supported by theis
statement as well.
默认 VS2017 使用旧版本 C#
编译器。您可以通过从 NuGet 安装 Microsoft.Net.Compilers
来启用 C# 7.0
功能,这可用于使用最新版本的编译器编译代码。