C#闭包的陷阱

1、什么是闭包?

    闭包最开始在JS出现的比较多,意思是“通过一系方法,将函数内部的变量(局部变量)转化为全局变量。”其实C#也有闭包的概念:内层的函数可以引用包含在它外层的函数的变量,即使外层函数的执行已经终止。但该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值。说得云里雾里,我们下面具体例子看看。.

2、陷阱

查看如下代码

{    Action[] act = new Action[6];    for (var i = 0; i < act.Length; i++)    {        act[i] = () =>        {            string str = "dotnet开发跳槽"+i;            Console.WriteLine(str);        };    }    foreach (var item in act)    {        item();    }}

输出结果:

C#闭包的陷阱

怎么会输出“dotnet开发跳槽6”呢?这就是闭包带来的问题。

3、为啥会这样?

    在for循环中,只能有一个 i 变量。即在第一次循环时,i 的地址就分配好了,不会因为循环次数的多少而发生任何改变,其改变的只能是里面装载的值。当使用匿名方法时传进去的是变量的地址,不是具体值。只有当真正执行这个匿名方法时,才会去确定它的值。这就是为什么上面的例子中,其结果均为“dotnet开发跳槽6”。

4、怎么避免闭包陷阱

    在for循环中增加一个临时变量t,用来替换i,这时候每次循环编译器会对这个t重新分配内存,就不会出现这个问题了。代码如下:

{    Action[] act = new Action[6];    for (var i = 0; i < act.Length; i++)    {        int t = i;        act[i] = () =>        {            string str = "dotnet开发跳槽"+ t;            Console.WriteLine(str);        };    }    foreach (var item in act)    {        item();    }}

C#闭包的陷阱

5、闭包的优点

    闭包的存在也不是没有意义,在winform开发中,使用闭包中可以轻松的访问外层函数定义的变量。winform的开发者可以试一试。