meJustAndrew meJustAndrew - 3 months ago 26
C# Question

Does closure involves boxing?

After I asked this question and read this recommended article about closures, I have started to ask myself that if making closure in C# involves boxing.

As it said in the article , this lines of code will create a closure by making

myVar
exist outside it's scope :

public static Func<int,int> GetAFunc()
{
var myVar = 1;
Func<int, int> inc = delegate(int var1)
{
myVar = myVar + 1;
return var1 + myVar;
};
return inc;
}


As explained there, an object of a compiler generated class is instantiated to carry the value of this variable outside it's scope. Here comes my question: Since the variable is indeed located on stack, making it part of an object, doesn't mean that closures involves boxing?

Answer

Since the variable is indeed located on stack, making it part of an object, doesn't mean that closures involves boxing?

The number of mistakes in that sentence is large. Let's disabuse you of some myths.

(1) Variables of value type do not "go on the stack". Variables that have short lifetimes go on the stack. Does this variable have a short lifetime? No. It has an arbitrarily long lifetime. So does it go on the stack? No.

(2) A field of an object of reference type is not on the stack. It's on the heap. Why? Because, again, a field has an arbitrarily long lifetime.

(3) An integer that is in a variable that is on the heap does not need to be boxed. Whether the integer is boxed or not depends on whether the integer has been converted to a reference type or not. The location of the variable is irrelevant; what matters is whether the type of the variable is a reference type or a value type.

So let's look at your code:

public static Func<int,int> GetAFunc()
{
    var myVar = 1;
    Func<int, int> inc = delegate(int var1)
                            {
                                myVar = myVar + 1;
                                return var1 + myVar;
                            };
    return inc;
}

This code is equivalent:

private class Closure
{ 
  public int myVar;
  public int SomeFunction (int var1)
  {
    this.myVar = this.myVar + 1;
    return var1 + this.myVar;
  }
}
public static Func<int,int> GetAFunc()
{
    Closure locals = new Closure();
    locals.myVar = 1;
    Func<int, int> inc = locals.SomeFunction;
    return inc;
}

Is there any time in there when an integer is converted to a reference type? No. So, no boxing.

HOWEVER remember, the point of avoiding boxing is avoiding the cost of allocating an extra object. We did allocate an extra object: the closure! There is no boxing penalty here, but there is a penalty due to the closure. Allocating the closure increases collection pressure. And of course, all references to the local must now go through an extra indirection.