20.7 闭包的概念。内层函数引用的外层函数的变量的最终值。
我们预期的输出是, jiejiep, xiaoyi, zhangzetian
但是实际我们运行后发现,程序会报错,提示索引超出界限。
为什么没有达到我们预期的效果呢?让我们再来看一下闭包的概念。内层函数引用的外层函数的变量的最终值。就是说,当线程中执行方法时,方法中的i参数的值,始终是userList.Count。原来如此,那我们该如何
避免闭包陷阱呢?C#中普遍的做法是,将匿名函数引用的变量用一个临时变量保存下来,然后在匿名函数中使用临时变量。
List<UserModel> userList = new List<UserModel>
{
new UserModel{ UserName="jiejiep", UserAge = 26},
new UserModel{ UserName="xiaoyi", UserAge = 25},
new UserModel{ UserName="zhangzetian", UserAge=24}
};
for (int i = 0; i < 3; i++)
{
UserModel u = userList[i];
ThreadPool.QueueUserWorkItem((obj) =>
{
//TODO
//Do some process...
//...
Thread.Sleep(1000);
//UserModel u = userList[i];
Console.WriteLine(u.UserName);
});
}
5.5 匿名方法中的捕获变量
static MethodInvoker CreateInvoker()
{
int count = 5;
MethodInvoker ret = delegate
{
Console.WriteLine(count);
count++;
};
ret();
return ret;
}
5.5.4 捕获变量的延长生存期
static void Main(string[] args)
{
MethodInvoker m = CreateInvoker();
m();
m();
Console.ReadKey();
}
static MethodInvoker CreateInvoker()
{
int count = 5;
MethodInvoker ret = delegate
{
Console.WriteLine(count);
count++;
};
ret();
return ret;
}
由循环的初始部分声明的变量只被实例化一次。这很容易弄错!如果你想捕获循环变量在一次特定的循环迭代中的值,必须在循环内部引入另一个变量,并将循环变量的值复制给它,再捕捉那个新变量
static void Main(string[] args)
{
MethodInvoker m = CreateInvoker();
m();
m();
Console.ReadKey();
}
static MethodInvoker CreateInvoker()
{
int count = 5;
MethodInvoker ret = delegate
{
Console.WriteLine(count);
count++;
};
ret();
return ret;
}