IL_0000: newobj instance void AnonymousMethod.Program/'<>c__DisplayClass1'::.ctor() IL_0005: stloc.1 IL_0006: nop IL_0007: ldloc.1 IL_0008: ldc.i4.5 IL_0009: stfld int32 AnonymousMethod.Program/'<>c__DisplayClass1'::counter
通过上面的分析可以看到,编译器创建了一个额外的类来容纳变量,CreateDelegateInstance方法拥有该类的一个实例引用,并通过这个引用访问counter变量。counter这个局部变量并不是在"调用栈"空间上,这也就解释了为什么函数返回后,这个变量没有被销毁。
在上面的例子中只有一个委托实例,下面再看一个拥有多个委托实例的例子:
static void Main(string[] args) { List<Action> list = new List<Action>(); for(int index = 0; index < 5; index++) { int counter = index * 10; list.Add(delegate { Console.WriteLine(counter); counter++; }); } foreach (Action x in list) { x(); } list[0](); list[0](); list[1](); Console.Read(); }
代码输出为:
通过输出可以看到,每个委托实例将捕获不同的变量。
所以被捕获变量的声明期可以总结为:对于一个被捕获的变量,只要还有任何委托实例在引用它,它就会一直存在;当一个变量被捕获时,捕获的是变量的"实例"。
总结本文介绍了闭包和不同的变量类型。在匿名方法中,通过被捕获变量,我们可以使用"现有"的上下文信息,而不必专门设置额外的类型来存储一些已知的数据。
同时,介绍了被捕获变量的生命期,通过IL代码看到了被捕获变量的工作原理。