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

时间:2019-06-12 11:56:26

标签: c# boxing

is运算符(expr is constant)匹配的恒定模式documentation如下:

  

常量表达式的计算方式如下:

     
      
  1. 如果exprconstant是整数类型,则C#相等运算符将确定表达式是否返回true(即,是否expr == constant)。

  2. p>   
  3. 否则,表达式的值由对静态Object.Equals(expr, constant)方法的调用确定。

  4.   

因此,在使用此代码时

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
}

为什么会这样?

2 个答案:

答案 0 :(得分:5)

在所有情况下,编译器都是相同的-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

与问题中的预期代码相同

答案 1 :(得分:1)

is operator Documentation状态:

  

使用恒定模式执行模式匹配时,is测试   表达式是否等于指定的常量。 C# 6及更低版本中   版本,switch语句支持常量模式。   从C# 7.0开始,is语句也支持它。

默认情况下,VS2017使用旧版本C#编译器。您可以通过从NuGet安装C# 7.0来启用Microsoft.Net.Compilers功能,该功能可用于使用最新版本的编译器来编译代码。