Search code examples
c#pass-by-referencevalue-typeboxingreference-type

Boxing value type to send it to a method and get the result


I'm curious about how C# behaves when passing value/reference types into a method. I'd like to pass a boxed value type into the method "AddThree". The idea is to get in the caller function (Main) the result of the operations performed within "AddThree".

static void Main(string[] args)
{
    int age = 3;
    object myBox = age;
    AddThree(myBox);
    // here myBox = 3 but I was expecting to be  = 6
}

private static void AddThree(object age2)
{
    age2 = (int)age2 + 3;
}

I've tried this with pure reference types like string and I get the same result. If I "wrap" my int within a class and I use the "wrap" class in this example it works as I'm expecting, ie, I get myBox = 6. If I modify "AddThree" method signature to pass the parameter by ref, this also returns 6. But, I don't want to modify the signature or create a wrapper class, I want to just box the value.


Solution

  • I don't want to modify the signature or create a wrapper class, I want to just box the value.

    Then that will be a problem. Your method passes a boxed int, then unboxes it and adds 3 to the local age2, which causes another boxing operation, and then throws away the value. De-facto, you're assinging age2 to two different objects on the heap, they do not point to the same object. Without modifying the method signature, this won't be possible.

    If you look at the generated IL for AddThree, you'll see this clearly:

    AddThree:
    IL_0000:  nop         
    IL_0001:  ldarg.0     
    IL_0002:  unbox.any   System.Int32 // unbox age2
    IL_0007:  ldc.i4.3    // load 3
    IL_0008:  add         // add the two together
    IL_0009:  box         System.Int32 // box the result
    IL_000E:  starg.s     00 
    IL_0010:  ret    
    

    You unbox the value, add 3 and then box the value again, but you never return it.

    To visualize this case further, try returning the newly boxed value from the method (just for the sake of the test), and use object.ReferenceEquals to compare them both:

    static void Main(string[] args)
    {
        int age = 3;
        object myBox = age;
        var otherBox = AddThree(myBox);
        Console.WriteLine(object.ReferenceEquals(otherBox, myBox)); // False
    }
    
    private static object AddThree(object age2)
    {
        age2 = (int)age2 + 3;
        return age2;
    }