为什么 is-operator 会导致不必要的装箱?

Why does the is-operator cause unnecessary boxing?

常量模式与 is 运算符 (expr is constant) 匹配的 documentation 指出:

The constant expression is evaluated as follows:

  1. If expr and constant are integral types, the C# equality operator determines whether the expression returns true (that is, whether expr == constant).

  2. 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

这与问题中的预期代码相同

is operator Documentation 状态:

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 功能,这可用于使用最新版本的编译器编译代码。