Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the is-operator cause unnecessary boxing?

Tags:

c#

boxing

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?

like image 729
Thomas Flinkow Avatar asked Jun 12 '19 11:06

Thomas Flinkow


2 Answers

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

like image 52
Panagiotis Kanavos Avatar answered Nov 15 '22 22:11

Panagiotis Kanavos


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.

like image 20
SᴇM Avatar answered Nov 16 '22 00:11

SᴇM