【问题标题】:Why does the is-operator cause unnecessary boxing?为什么 is-operator 会导致不必要的装箱?
【发布时间】:2019-10-26 22:30:37
【问题描述】:

documentationis 运算符 (expr is constant) 的常量模式匹配状态:

常量表达式的求值如下:

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

  2. 否则,表达式的值由调用静态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
}

但是,在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
}

为什么会这样?

【问题讨论】:

  • 也许this 有帮助
  • @SᴇM 你用的是什么编译器?对我来说,VS17 和 SharpLab(参见 here)都会产生问题中存在的 CIL。
  • @ThomasFlinkow 您使用什么框架版本?我刚刚注意到,如果我从 4.6.1 更改为 4,它会将值框起来。
  • @ThomasFlinkow 你可以使用 Sharplab.io 来尝试不同的编译器。你是对的,旧的 Roslyn 版本做盒子,而实现 C# 8 功能的 newer 没有
  • 抱歉,这只是 VS 版本之间的区别(还有 c# 版本)。我相信这是因为新的Pattern Matching 功能,它显然在没有装箱的情况下检查值类型的类型。

标签: c# boxing


【解决方案1】:

编译器在所有情况下都是相同的 - Roslyn。不过,不同的版本会产生不同的 IL。 C# 8 版本不装箱,而旧版本则装箱。

例如,2.9.0 这个 sn-p 的 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

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

【讨论】:

    【解决方案2】:

    is operator Documentation 状态:

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

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

    【讨论】:

    • @Panagiotis Kanavos 不,这不是真的,我选择了C# 版本7.0,它生成的IL 没有装箱。即使将版本更改为6.0,它也会产生编译器错误:"Feature 'pattern matching' is not available in C# 6. Please use language version 7.0 or above."
    • 我检查过,即使我使用 C# 7.3(这是我的 VS17 版本中最新的可选版本),它仍然会生成装箱指令。我认为这可能与 VS19 有关。
    • @ThomasFlinkow 我猜 VS2017 默认不使用新编译器的新功能,我注意到,如果您从 NuGet 安装 Microsoft.Net.Compilers,它将使用新编译器而不是 VS2017默认一个。另外,请查看有关What is the Purpose of Microsoft.Net.Compilers?的帖子
    • 谢谢。我会尽快试试这个包。我赞成你的回答,因为你真的想帮助我,我很感激。
    • @ThomasFlinkow 不客气!是的,我也很好奇。
    猜你喜欢
    • 1970-01-01
    • 2018-07-30
    • 1970-01-01
    • 1970-01-01
    • 2017-04-26
    • 2010-11-10
    • 1970-01-01
    • 2013-02-18
    相关资源
    最近更新 更多