1

I have the following method:

public static bool EquivalentTo<T>(this T? current, T? compare)
    where T : class
{
    if (current is null && compare is null)
        // both are null
        return true;

    if (current is null || compare is null)
        // one is null, but not both
        return false;

    return current.Equals(compare);
}

IL Spy gives the following IL in Release build:

.method public hidebysig static 
    bool EquivalentTo<class T> (
        !!T current,
        !!T compare
    ) cil managed aggressiveinlining 
{
    .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
        01 00 00 00
    )
    .param type T
        .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = (
            01 00 01 00 00
        )
    // Method begins at RVA 0x4971
    // Header size: 1
    // Code size: 54 (0x36)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: box !!T
    IL_0006: brtrue.s IL_0012

    IL_0008: ldarg.1
    IL_0009: box !!T
    IL_000e: brtrue.s IL_0012

    IL_0010: ldc.i4.1
    IL_0011: ret

    IL_0012: ldarg.0
    IL_0013: box !!T
    IL_0018: brfalse.s IL_0022

    IL_001a: ldarg.1
    IL_001b: box !!T
    IL_0020: brtrue.s IL_0024

    IL_0022: ldc.i4.0
    IL_0023: ret

    IL_0024: ldarg.0
    IL_0025: box !!T
    IL_002a: ldarg.1
    IL_002b: box !!T
    IL_0030: callvirt instance bool [System.Runtime]System.Object::Equals(object)
    IL_0035: ret
} // end of method Extensions::EquivalentTo

I compiled with Visual Studio 2022 and .Net 8.0.100-preview.7.23376.3 in 64-bit.

I don't understand why there is boxing when the generic type is constrained to a class and thus cannot be a value type. What am I missing?

2
  • 3
    CIL isn't optimized at compile-time, optimization is a jitter duty and happens at runtime. Use Debug > Windows > Disassembly to see that the box generates no code. Commented Sep 10, 2023 at 17:13
  • I guess Roslyn just doesn't perform this optimization, but it shouldn't affect performance since boxing a class is no-op and will be eliminated by JIT. Only downside is that each instruction adds 5 byte to result assembly, which may be significant if a lot of such checks are performed Commented Sep 11, 2023 at 3:51

1 Answer 1

2

box !!T br.true or br.false sequence is always a no-op for structs.

Since the JITter compiles separate code for all struct (valuetype) generic code, it can see that a box would never result in a null, so it is elided. In the case of classes (reference types) it just does a normal null check.

You can see this if you look at the JIT ASM machine code that is generated for both structs and classes.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.