Eric Lippert's answer really hits the point. However, it would be nice to create a picture of how the frames and frames of the stack work. To do this, it helps to look at a slightly more complex example.
Here is the capture code:
public class Scorekeeper { int swish = 7; public Action Counter(int start) { int count = 0; Action counter = () => { count += start + swish; } return counter; } }
And here is what I think the equivalent would be (if we are lucky, Eric Lippert will comment on whether this is really correct or not):
private class Locals { public Locals( Scorekeeper sk, int st) { this.scorekeeper = sk; this.start = st; } private Scorekeeper scorekeeper; private int start; public int count; public void Anonymous() { this.count += start + scorekeeper.swish; } } public class Scorekeeper { int swish = 7; public Action Counter(int start) { Locals locals = new Locals(this, start); locals.count = 0; Action counter = new Action(locals.Anonymous); return counter; } }
The fact is that the local class replaces the entire frame of the stack and is initialized accordingly each time the Counter method is called. Typically, a stack frame includes a reference to 'this', plus method arguments, as well as local variables. (The stack frame also works when the control unit is entered.)
Therefore, we do not have only one object corresponding to the captured context; instead, we actually have one object for each captured frame of the stack.
Based on this, we can use the following mental model: the frames of the stack are stored on the heap (and not on the stack), while the stack itself contains pointers to the frames of the stack that are on the heap. Lambda methods contain a pointer to the stack stack. This is done using managed memory, so the frame overlaps the heap until it is no longer needed.
Obviously, the compiler can implement this using only the heap when the heap object is required to support lambda closure.
What I like about this model is the integrated picture for yield return. We can think of the iterator method (using return return) as if it were created on the heap, and the link pointer stored in the local variable in the caller for use during iteration.
sjb-sjb Oct 25 '17 at 4:26 2017-10-25 04:26
source share