Why does the is-operator cause unnecessary boxing?
Asked Answered
C

2

15

The documentation of constant pattern matching with the is-operator (expr is constant) states:

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.


Therefore, when using this code

public bool IsZero(int value)
{
    return value is 0;
}

I expect it to use the == operator (case 1) and generate this code:

.method public hidebysig instance bool
    IsZero(
       int32 'value'
    ) cil managed
{
    .maxstack 8

    ldarg.1
    ldc.i4.0
    ceq
    ret
}

However, in reality, the integer parameter and the constant (literal) are boxed in order to be passed to the static Object.Equals method (case 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
}

Why is that the case?

Canella answered 12/6, 2019 at 11:56 Comment(9)
Maybe this helpsOversight
Are you sure you were examining correct IL code? I've tried and there was no boxing.Gabrila
@SᴇM what compiler did you use? For me, both VS17 and SharpLab (see here) produce the CIL present in the question.Canella
@ThomasFlinkow I'm using VS19. Can you share the whole IL code of IsZero method?Gabrila
@SᴇM I edited the question to include the whole method code. I removed only comments and the labels.Canella
@ThomasFlinkow What framework version do you use? I've just noticed if I change from 4.6.1 to 4 it boxes the value.Gabrila
@ThomasFlinkow you can use Sharplab.io to try different compilers. You are correct, older Roslyn versions do box while the newer that implement C# 8 features don'tHenceforward
@SᴇM I used 4.7.2 with VS17... and it still boxes.Canella
Sorry, it was just the difference between VS versions (and c# versions too). I believe that's because of new Pattern Matching feature, which apparently checking value type's type without boxing.Gabrila
H
7

The compiler is the same in all cases - Roslyn. Different versions produce different IL though. The C# 8 versions don't box, while older ones do.

For example, with 2.9.0 the IL for this snippet :

using System;
public class C {

    public bool IsZero(int value)
    {
        return value is 0;
    }
}

is

    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

Using any of the C# 8 versions though produces this in debug mode :

    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

and this in Release.

    IL_0000: ldarg.1
    IL_0001: ldc.i4.0
    IL_0002: ceq
    IL_0004: ret

That's the same as the expected code in the question

Henceforward answered 12/6, 2019 at 12:26 Comment(0)
D
1

is operator Documentation states:

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.

By default VS2017 using older version C# compiler. You can enable C# 7.0 features by installing Microsoft.Net.Compilers from NuGet which can be used to compile the code with the latest version of the compiler.

Dyedinthewool answered 12/6, 2019 at 13:24 Comment(7)
@Panagiotis Kanavos No that's not true, I've selected C# version 7.0 and it generates IL without boxing. Even though, if you change version to 6.0 it will generate compiler error: "Feature 'pattern matching' is not available in C# 6. Please use language version 7.0 or greater."Gabrila
I checked and even when I used C# 7.3 (that's the latest selectable in my VS17 version) it still generates the boxing instructions. It might likely have something to do with VS19, I think.Canella
@ThomasFlinkow Can you provide more details about how you checked that?Gabrila
I went to Project Properties > Build > Advanced > Language Version and selected C# 7.3. For the record, my VS version is 15.9.12 and the compiler (I assume that is the compiler) reports as C# tools version 2.10.0 beta2 in the Help > About Visual Studio window.Canella
@ThomasFlinkow I guess VS2017 by default is not using new features of the new compiler and I've noticed, that if you install Microsoft.Net.Compilers from NuGet it will use new compiler instead of the VS2017 default one. Also, check this post about What is the Purpose of Microsoft.Net.Compilers?Gabrila
Thank you. I will try the package asap. I upvoted your answer as you really tried to help me, and I appreciate that.Canella
@ThomasFlinkow you're welcome! Yeah, I was curious too.Gabrila

© 2022 - 2024 — McMap. All rights reserved.