浅谈.NET中闭包

什么是闭包

    闭包可以从而三个维度来说明。在编程语言领域,闭包是指由函数以及与函数相关的上下文环境组合而成的实体。通过闭包,函数与其上下文变量之间建立起关联关系,上下文变量的状态可以在函数的多次调用过程中持久保持。从作用域而言,私有变量的生命周期被延长,函数调用所生成的值在下次调用时仍被保持。从安全性而言,闭包有利于信息的隐蔽,私有变量只在该函数内可见。

.NET中的闭包

    说起闭包,可对会想起JavaScript。在JavaScript语言中,闭包可以说是无处不在。同样,.NET中也有闭包。只不过实现方式和JavaScript不太一样。

通常而言,形成闭包有一些值得总结的非必要条件:

  • 嵌套定义函数
  • 匿名函数
  • 将函数作为参数或返回值

    在.NET中,函数并不是第一级成员,所以并不能像JavaScript那样通过嵌套子函数的方式实现闭包。但.NET可以通过匿名委托形成闭包。

 delegate void HelloDelegate();
        static void Main(string[] args)
        {
            string str = "Hello World!";

            HelloDelegate hello = delegate()
            {
                Print(str);
            };
        }

        private static void Print(string str)
        {
            Console.WriteLine(str);
        }

反编译上面的IL代码:

S)TV14[6U~P`@NY6G~$}2UK

如上图所示,编译器自动生成了一个内部类,变量strb变成这个类的字段,即使创建该变量的方法(Main)执行结束,该变量也不会释放,而是在所有回调函数执行之后才被GC回收。这就是.NET实现闭包的原理。

闭包带来的问题

如下面代码:

 static void Main()
        {
            IList<Action> actions = new List<Action>();

            for (int i = 0; i < 5; i++)
            {
                actions.Add(() => Console.WriteLine(i));
            }

            foreach (var action in actions)
            {
                action();
            }
        }

先猜猜输入的值是什么,如果猜的0、1、2、3、4的话就错了。应该全是4。那为什么呢?因为闭包具有延迟的和数据共享的特性,只有当调用action()方法时才会获取i的值,这是i的值经过i++已经变成4,又因为所有的action都会获取同一个i值,所以最后输出的值都为4。

那怎么解决呢?通常解决方法时在循环中加入中间量。

            for (int i = 0; i < 5; i++)
            {
                int j = i;
                actions.Add(() => Console.WriteLine(j));
            }

N}$8(%MX}MUPZ}YVDY3GGJB

原文地址:https://www.cnblogs.com/Khadron/p/Closure.html