views:

117

answers:

3

This is a question based on the article "Closing over the loop variable considered harmful" by Eric Lippert.
It is a good read, Eric explains why after this piece of code all funcs will return the last value in v:

 var funcs = new List<Func<int>>();
 foreach (var v in values)
 {
    funcs.Add(() => v);
 }

And the correct version looks like:

 foreach (var v in values)
 {
    int v2 = v;
    funcs.Add(() => v2);
 }

Now my question is how and where are those captured 'v2' variables stored. In my understanding of the stack, all those v2 variables would occupy the same piece of memory.

My first thought was boxing, each func member keeping a reference to a boxed v2. But that would not explain the first case.

+3  A: 

Hi Henk,

Jon Skeet has a good explanation of this over here, where he points to a blog article too.

[Edit] - per great comment from @AnthonyWJones (edits in italic)

The captured variables are stored inside a class wrapper behind the scenes - essentially a new wrapper object is created for each set of captured variable*s used by the delegate*. This is so that a hard reference keeps the object*s* alive and prevents garbage collection while the delegate is still active.

It's not quite boxing, although that is probably a confusing analogy.

Think of it as the variable being captured, and not so much as the value.

Neil Fenwick
Some adjustment to wording needed. If there were two variables that were captured per iteration how many "wrapper" objects would be needed in each iteration? Ans: 1. Now re-read your answer.
AnthonyWJones
But why local temporary variable behaves differently from foreach variable? They both are value types located in stack so behaviour should be the same.
Konstantin Spirin
The point is that the variable isn't located in the stack. How could it, if it outlives the lexical scope in which it was created?
jleedev
@Konstantin I was also confused by value-type thinking originally. Don't think of the value, think of the variable. Unless you declare a new variable for each captured use inside a delegate, ie a new "handle", you are just holding a reference to the "v" variable and that is only declared once in the for loop.
Neil Fenwick
@AnthonyWJones thanks for comment re wording. I'd forgotten to think about more than just the simple case of just one variable in the scope.
Neil Fenwick
+5  A: 

Ordinarily the variable v2 would be allocated some space on the stack at the start of the code block its found in. At the end of the code block (i.e. the end of the iteration) the stack is wound back (I'm describing the logical scenario not an optimised actual behaviour). Hence each v2 is in effect a different v2 from the previous iteration although its true that it would end up occupying the same memory location.

However the compiler spots that v2 is being used by an anonymous function created by the lambda. What the compiler does is hoist the v2 variable. The compiler creates a new class that has an Int32 field to hold the value of v2, it is not allocated a place on the stack. It also makes the anonymous function a method of this new type. (For simplicity I'll give this un-named class a name, lets call it "Thing").

Now in each iteration a new instance of "Thing" is created and when v2 is assigned its the Int32 field which is actually assigned not just a point in stack memory. The anonymous function expression (the lambda) will now return a delegate which has non-null instance object reference, this reference will be to the current instance of "Thing".

When the delegate for anonymous function is invoked it will execute as an instance method of a "Thing" instance. Hence v2 is available as a member field and will have the value give it during the iteration this instance of "Thing" was created.

AnthonyWJones
+4  A: 

Further to the answers from Neil and Anthony, here's an example of the code that might be auto-generated in both cases.

(Note that this is only to demonstrate the principle, the actual compiler-generated code won't look exactly like this. If you want to see the real code then you can take a look using Reflector.)

// first loop
var captures = new Captures();
foreach (var v in values)
{
    captures.Value = v;
    funcs.Add(captures.Function);
}

// second loop
foreach (var v in values)
{
    var captures = new Captures();
    captures.Value = v;
    funcs.Add(captures.Function);
}

// ...

private class Captures
{
    public int Value;

    public int Function()
    {
        return Value;
    }
}
LukeH
+1 Nice and simple.
Groo