Friday, September 03, 2010

09/03: Closure in Anonymous /Lambda Expression

 

1. What is closure ?

Closure is a concept coming from functional programming. It can use variables which is outside of scope of current context.( in simple word, using  variables declared outside inside lambda expression ).

闭包是将一些执行封装,将它像对象一样传递,在传递时,执行依然能够访问到原上下文。访问原来上下文,是闭包的重要特征

eg.

 

int mulitplyby = 2;

Func<int, int> operation = x => x * mulitplyby

//mulitplyby variable inside the lambda expression although it is declared outside the scope of the expression. This concept is called variable capture. C# complier will take these captured variables and put in a new generated helper class  .

2. Confusion/Problem caused by closure

 

class Program
    {
        static void Main(string[] args)
        {
            List<Action> ls = new List<Action>();
            List<Action<int>> ls2 = new List<Action<int>>();

            for (int i = 0; i < 10; i++)
            {
                Action act = () =>
                {
                    Console.WriteLine(i);
                };

                Action<int> act2 = (sdy) =>
                {
                    int tcp = sdy;
                    Console.WriteLine(tcp);
                };

                ls.Add(act);
                ls2.Add(act2);
            }

            foreach (Action action in ls)
            {
                action();
            }

            foreach (Action<int> action in ls2)
            {
                for (int i = 0; i < 10; i++)
                {
                    action(i);
                }
            }

            System.Console.Read();
        }
    }

// First result:  由于只声明了一个i变量
所以所有的Action捕获的都是同一个i变量。结果就是每一行都输出数字10

 

// Second Result : 式实现了输出0到9

之所以使用tcp可以得到正确的结果,是因为tcp每次都重新开辟空间。

与上例代码的唯一不是是在循环体中使用了一个局部变量tp,这种写法在通常看来不通是多用了一个中转变量,对程的执行不会有什么影响,但事实上tp这个变量在被每个Action独立保存. 这样,每次循环体在执行的时候,都会取得一个全新的tp,而且tp不会因为所在声名体的完成而出栈

 

 

 

3.匿名方法中的变量 Scope:

 

若匿名方法中如果引用了某个outside 变量,则该局部变量将被提升为实例变量,并储存于一个叫做闭包(closure)的对象中。提升之后,即使创建该变量的方法执行完毕该变量仍不会消亡。 当指向该匿名函数的所有引用都消失后,该闭包变量即可正常地被垃圾回收器回收

eg.

        delegate int wxd(int i);
       delegate wxd lzm(int ii);

       static void Main(string[] args)
       {
           lzm obj = delegate(int ii)
           {
               return
               delegate(int i)
               {
                   return i + ii;
               };
           };
           wxd w1 = obj(1);
           wxd w2 = obj(2);

           System.Console.WriteLine(w1(3));
           System.Console.WriteLine(w2(3));

           System.Console.Read();
       }

 

// Return 4 , 5

通常理解,函数的参数是放在栈中的。
如果闭包也将参数放在栈中,那么[ii]在[obj]运行结束的时候就会消失掉,这个时候[w1,w2]再通过栈去搜索[ii]显然就是不可能的。
所以闭包中参数或内部变量不能放在栈中.而是放在程序执行过程之中的一张全局表里. [[obj]在返回内部函数的时候,将全局表,自己的结构表,内部函数的指针一起传递给变量[w1,w2].
这时内部函数可以访问[ii],外部却无法访问[ii]

0 Comments:

Post a Comment

<< Home