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; }