views:

228

answers:

3

Anyone any idea why a generic method which constrains T to class would have boxing instructions in the generates MSIL code?

I was quite surprised by this since surely since T is being constrained to a reference type the generated code should not need to perform any boxing.

Here is the c# code:

protected void SetRefProperty<T>(ref T propertyBackingField, T newValue) where T : class
{
    bool isDifferent = false;

    // for reference types, we use a simple reference equality check to determine
    // whether the values are 'equal'.  We do not use an equality comparer as these are often
    // unreliable indicators of equality, AND because value equivalence does NOT indicate
    // that we should share a reference type since it may be a mutable.

    if (propertyBackingField != newValue)
    {
        isDifferent = true;
    }
}

Here is the generated IL:

.method family hidebysig instance void SetRefProperty<class T>(!!T& propertyBackingField, !!T newValue) cil managed
{
    .maxstack 2
    .locals init (
        [0] bool isDifferent,
        [1] bool CS$4$0000)
    L_0000: nop 
    L_0001: ldc.i4.0 
    L_0002: stloc.0 
    L_0003: ldarg.1 
    L_0004: ldobj !!T
    L_0009: box !!T
    L_000e: ldarg.2 
    L_000f: box !!T
    L_0014: ceq 
    L_0016: stloc.1 
    L_0017: ldloc.1 
    L_0018: brtrue.s L_001e
    L_001a: nop 
    L_001b: ldc.i4.1 
    L_001c: stloc.0 
    L_001d: nop 
    L_001e: ret 
}

Notice the box !!T instructions.

Anyone any idea why this is being generated?

Anyone any ideas how to avoid this?

Thanks, Phil

A: 

I believe this is intended by design. You're not constraining T to a specific class so it's most likely down casting it to object. Hence why you see the IL include boxing.

I would try this code with where T : ActualClass

Chris Marisic
If you do T : ActualClass, why bother with the generics?
Robert Harvey
Because you can constrain T to higher levels... like iSomeInterface...
Chris Marisic
Chris, if T were an object, wouldn't it have already been boxed prior to pushing on the stack? Why then would any boxing operation need to be performed on it?I would expect the == operator to check reference equality if T were an object, so this also would not require un/boxing operations.
Phil
+1  A: 

I'm not sure why any boxing is ocurring. One possible way to avoid the boxing is to not use it. Just recompile without the boxing. Ex:

.assembly recomp_srp
{
    .ver 1:0:0:0
}

.class public auto ansi FixedPBF
{

.method public instance void .ctor() cil managed
{

}

.method hidebysig public instance void SetRefProperty<class T>(!!T& propertyBackingField, !!T newValue) cil managed
{
 .maxstack 2    
  .locals init ( bool isDifferent, bool CS$4$0000)

  ldc.i4.0
  stloc.0
  ldarg.1
  ldobj !!T
  ldarg.2
  ceq
  stloc.1
  ldloc.1
  brtrue.s L_0001
  ldc.i4.1
  stloc.0
  L_0001: ret

}

}

...if you save to a file recomp_srp.msil you can simply recompile as such:

ildasm /dll recomp_srp.msil

And it runs OK without the boxing on my end:

        FixedPBF TestFixedPBF = new FixedPBF();

        TestFixedPBF.SetRefProperty<string>(ref TestField, "test2");

...of course, I changed it from protected to public, you would need to make the change back again and provide the rest of your implementation.

Robert Venables
+1  A: 

You don't have to worry about any performance-degradations from the box instruction because if its argument is a reference type, the box instruction does nothing. Though it's still strange that the box instruction has even been created (maybe lazyiness/easier design at code generation?).

Thomas Danecker