持续更新中…
引言
看过一些文章,总结下来就是,闭包是一个带共享数据的函数。内层的函数(子函数)可以引用包含在它外层函数(父函数)的变量,即使外层函数的执行已经终止。但该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值。嵌套定义函数,使用了外部定义域(父函数定义域)(非全局定义域)的变量。是否有返回值并不影响判断。
javascript的闭包理解:函数可以使用函数之外定义的变量。
示例
这三个函数几乎一样,唯一不同的就是action函数里变量。
函数action => Console.WriteLine(x)
关联的变量是x
。这个匿名函数以该形式保存。直至被调用时,才会去取对应的i
。
private static void F1()
{
IList<Action> actions = new List<Action>();
for (int i = 0; i < 5; i++)
{
actions.Add(() => Console.WriteLine(i));
}
foreach (var action in actions)
{
action();
}
}
ouput:5 5 5 5 5
这个例子比较容易理解。循环结束后,i=5
。所以去遍历actions
时,所有的action => Console.WriteLine(x)
使用的是变量i
,因此结果是 5 5 5 5 5。
private static void F2()
{
IList<Action> actions = new List<Action>();
for (int i = 0; i < 5; i++)
{
int j = i;
actions.Add(() => Console.WriteLine(j));
}
foreach (var action in actions)
{
action();
}
}
ouput:0 1 2 3 4
这里j
是循环里的局部变量,并不受i++
的影响。action => Console.WriteLine(x)
使用的是变量j
,在每次循环后就已经固定下来。因此结果是 0 1 2 3 4。
private static void F3()
{
int j;
IList<Action> actions = new List<Action>();
for (int i = 0; i < 5; i++)
{
j = i;
actions.Add(() => Console.WriteLine(j));
}
foreach (var action in actions)
{
action();
}
}
ouput:4 4 4 4 4
这个的思路与F1类似。只是因为循环结束后,i=5
,j=4
。因此结果是 4 4 4 4 4。但与F2做比较,就能发现他们的重大区别,很有参考意义。
应用
上面的例子只是一种很标准的闭包格式。其实很多时候我们在使用lambda表达式时候,已经属于闭包范围了。如有一个list数据,我们希望通过多线程,每个线程处理其中的一个对象。于是有如下正确与错误的示例。
List<UserModel> userList = new List<UserModel>
{
new UserModel{ UserName="A", UserAge = 1},
new UserModel{ UserName="B", UserAge = 2},
new UserModel{ UserName="C", UserAge = 3}
};
// 错误
for (int i = 0; i < 3; i++)
{
ThreadPool.QueueUserWorkItem((obj) =>
{
Thread.Sleep(100);
UserModel u = userList[i];// i永远都是userList.Count
Console.WriteLine("case1:\t" + u.UserName);
// 等价于
// Console.WriteLine("case1:\t" + userList[i].UserName);
});
}
// 正确
for (int i = 0; i < 3; i++)
{
UserModel u = userList[i];
ThreadPool.QueueUserWorkItem((obj) =>
{
Thread.Sleep(100);
Console.WriteLine("case2:\t" + u.UserName);
});
}