The easiest way of finding out how it's implemented is to try it. Write some code which uses a captured variable, compile it, then look at it in Reflector. Note that it's the variable which is captured, not the value. That's one of the big differences between Java and C# in this area.
The basic idea is that each level of scope containing at least one captured variable results in a new class with fields for the variables which have been captured. If there's more than one level, then an inner scope also has a field for the next scope out, and so on. The genuine local variables on the stack end up being references to instances of the autogenerated classes.
Here's an example:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<Action> actions = new List<Action>();
for (int i=0; i < 5; i++)
{
int copyOfI = i;
for (int j=0; j < 5; j++)
{
int copyOfJ = j;
actions.Add(delegate
{
Console.WriteLine("{0} {1}", copyOfI, copyOfJ);
});
}
}
foreach (Action action in actions)
{
action();
}
}
}
(You get different results if you don't take a copy of course - experiment!) This is compiled into code like this:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<Action> actions = new List<Action>();
for (int i=0; i < 5; i++)
{
OuterScope outer = new OuterScope();
outer.copyOfI = i;
for (int j=0; j < 5; j++)
{
InnerScope inner = new InnerScope();
inner.outer = outer;
inner.copyOfJ = j;
actions.Add(inner.Action);
}
}
foreach (Action action in actions)
{
action();
}
}
class OuterScope
{
public int copyOfI;
}
class InnerScope
{
public int copyOfJ;
public OuterScope outer;
public void Action()
{
Console.WriteLine("{0} {1}", outer.copyOfI, copyOfJ);
}
}
}
Every reference to the captured variable ends up going through the instance of the generated class, so it's not just a one-off copy. (Okay, in this case nothing else in the code uses the captured variables, but you can easily imagine it could.) Note how for any one iteration of the outer loop, the five new instances all share one instance of OuterScope
. You might want to try playing with extra code in the delegate to see how that affects things - if the delegate changes copyofI
that change will be seen in the next delegate; changes to copyOfJ
won't be seen because the next delegate will be using a separate instance of InnerScope
.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…