防止Lambda的各种坑爹(一)

   1.奇怪的被捕获的变量: 

View Code
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace TestLambda
 8 {
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             Action action = CreateAction();
14             action();
15             action();
16             action();
17             Console.Read();
18         }
19 
20         static Action CreateAction()
21         {
22             int value = 0;
23             Action action = () => Console.Write(value++);
24             return action;
25         }
26     }
27 }

  你觉得上面的代码会输出什么呢?我们一般会认为,由于静态方法有自己线程栈,value变量会在对应的栈上分配空间,所以一旦方法CreateAction执行完毕,CreateAction对应的线程栈被销毁,value变量也会随之消失...但是令人惊讶的是,第二次和第三次调用action委托的时候,value变量似乎依然保留这上次调用后的值。所以我们认为输出的结果是000。可是运行代码,输出的结果却是 012。
  奇怪吗?秘密就在于我们之前的那个假设上->value是真的分配在自己的线程栈上的吗?答案是否定的。事件上呢,编译器会为我们创建一个额外的类来存储变量。CreateAction拥有对该类的一个实例的引用,所以CreateAction可以访问到变量value。同时Action委托也有对该实例的引用。这样的话,除非Action委托和CreateAction方法同时被销毁的时候,变量value所在的实例才会被销毁。通过ILDASM,可以清楚的看到编译器所产生的类<>c_DisplayClass1。

   同时在CreateAction方法中可以找到对<>c_DisplayClass1的引用

  我想,如果用C#代码去表示的话,大概是这样的

View Code
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace TestLambda
 8 {
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             Action action = CreateAction();
14             action();
15             action();
16             action();
17 
18             var a = DynamicCreateAction();
19             a();
20             a();
21             a();
22 
23             Console.Read();
24         }
25 
26         static Action CreateAction()
27         {
28             int value = 0;
29             Action action = () => Console.Write(value++);
30             return action;
31         }
32 
33         static Action DynamicCreateAction()
34         {
35             DisplayClass d = new DisplayClass();
36 
37             Action action = new Action(d.Print);
38             return action;
39         }
40 
41         class DisplayClass
42         {
43             public int Value { get; set; }
44 
45             public void Print()
46             {
47                 Console.Write(Value++);
48             }
49         }
50 
51     }
52 
53 }

  OK,我想你现在明白了,"局部变量“不一定是局部的了吧。

 

posted @ 2012-12-02 14:25  gogo_baby  阅读(549)  评论(4编辑  收藏  举报