14

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?

Community
  • 1
  • 1
Thomas Flinkow
  • 4,845
  • 5
  • 29
  • 65
  • Maybe [this](https://www.danielcrabtree.com/blog/164/c-sharp-7-micro-benchmarking-the-three-ways-to-cast-safely) helps – JohnyL Jun 12 '19 at 12:05
  • Are you sure you were examining correct IL code? I've tried and there was no boxing. – SᴇM Jun 12 '19 at 12:07
  • 1
    @SᴇM what compiler did you use? For me, both VS17 and SharpLab (see [here](https://sharplab.io/#v2:C4LglgNgPgAgzAAhgJgQUQI4FcCGEDOAavgJL4ICwAUAN4L2VUNKIBGA9uxAmQFoCmAJ3YAKMADtgCAG54s/AJSNmNas2YwA7DLn8EYcgAYA3GoYBfauaA==)) produce the CIL present in the question. – Thomas Flinkow Jun 12 '19 at 12:09
  • @ThomasFlinkow I'm using VS19. Can you share the whole IL code of `IsZero` method? – SᴇM Jun 12 '19 at 12:10
  • @SᴇM I edited the question to include the whole method code. I removed only comments and the labels. – Thomas Flinkow Jun 12 '19 at 12:13
  • 2
    @ThomasFlinkow What framework version do you use? I've just noticed if I change from `4.6.1` to `4` it boxes the value. – SᴇM Jun 12 '19 at 12:20
  • 2
    @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't – Panagiotis Kanavos Jun 12 '19 at 12:22
  • @SᴇM I used `4.7.2` with VS17... and it still boxes. – Thomas Flinkow Jun 12 '19 at 12:43
  • 1
    Sorry, it was just the difference between VS versions (and `c#` versions too). I believe that's because of new [Pattern Matching](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7#pattern-matching) feature, which apparently checking value type's type without boxing. – SᴇM Jun 12 '19 at 12:48

2 Answers2

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

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
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.

SᴇM
  • 7,024
  • 3
  • 24
  • 41
  • @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."_ – SᴇM Jun 12 '19 at 13:52
  • 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. – Thomas Flinkow Jun 13 '19 at 04:22
  • @ThomasFlinkow Can you provide more details about how you checked that? – SᴇM Jun 13 '19 at 05:48
  • 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. – Thomas Flinkow Jun 13 '19 at 05:55
  • 1
    @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?](https://stackoverflow.com/questions/34533613/what-is-the-purpose-of-microsoft-net-compilers) – SᴇM Jun 13 '19 at 06:18
  • 1
    Thank you. I will try the package asap. I upvoted your answer as you really tried to help me, and I appreciate that. – Thomas Flinkow Jun 13 '19 at 06:42
  • 1
    @ThomasFlinkow you're welcome! Yeah, I was curious too. – SᴇM Jun 13 '19 at 06:46