5.5 匿名方法中的捕获变量
5.5.4 捕获变量的延长生存期
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 MethodInvoker m = CreateInvoker(); 6 m(); 7 m(); 8 9 Console.ReadKey(); 10 } 11 static MethodInvoker CreateInvoker() 12 { 13 int count = 5; 14 MethodInvoker ret = delegate 15 { 16 Console.WriteLine(count); 17 count++; 18 }; 19 ret(); 20 return ret; 21 } 22 }
5.5.5 局部变量实例化
使用多个委托来捕捉多个变量实例
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 List<MethodInvoker> list = new List<MethodInvoker>(); 6 for (int i = 0; i < 5; i++) 7 { 8 int a = i * 10; 9 list.Add(()=> 10 { 11 Console.WriteLine(a); 12 a++; 13 }); 14 } 15 16 Console.WriteLine("list======================"); 17 foreach (var item in list) 18 { 19 item(); 20 } 21 22 Console.WriteLine("list[0]======================"); 23 list[0](); 24 list[0](); 25 list[0](); 26 27 Console.WriteLine("list[1]======================"); 28 list[1](); 29 30 Console.ReadKey(); 31 32 /* 33 list====================== 34 0 35 10 36 20 37 30 38 40 39 list[0]====================== 40 1 41 2 42 3 43 list[1]====================== 44 11 45 */ 46 } 47 }
由循环的初始部分声明的变量只被实例化一次。这很容易弄错!
如果你想捕获循环变量在一次特定的循环迭代中的值,必须在循环内部引入另一个变量,
并将循环变量的值复制给它,再捕捉那个新变量
5.5.6 共享和非共享的变量混合使用
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 MethodInvoker[] delegates = new MethodInvoker[2]; 6 7 int outSide = 0; 8 9 for (int i = 0; i < 2; i++) 10 { 11 int inSide = 0; 12 delegates[i] = () => 13 { 14 Console.WriteLine("{0} {1}", outSide, inSide); 15 outSide++; 16 inSide++; 17 }; 18 } 19 20 MethodInvoker first = delegates[0]; 21 22 MethodInvoker second = delegates[1]; 23 24 first(); 25 first(); 26 first(); 27 28 Console.WriteLine("==================="); 29 30 second(); 31 second(); 32 33 Console.ReadKey(); 34 35 /* 36 0 0 37 1 1 38 2 2 39 =================== 40 3 0 41 4 1 42 */ 43 } 44 }
5.5.7 捕获变量的使用规则和小结
使用捕获变量时,请参照以下规则。
如果用或不用捕获变量时的代码同样简单,那就不要用。
捕获由 for 或 foreach 语句声明的变量之前,思考你的委托是否需要在循环迭代结束之后
延续,以及是否想让它看到那个变量的后续值。如果不是,就在循环内另建一个变量,
用来复制你想要的值。(在C# 5中,你不必担心 foreach 语句,但仍需小心 for 语句。)
如果创建多个委托实例(不管是在循环内,还是显式地创建),而且捕获了变量,思考一
下是否希望它们捕捉同一个变量。
如果捕捉的变量不会发生改变(不管是在匿名方法中,还是在包围着匿名方法的外层方
法主体中),就不需要有这么多担心。
如果你创建的委托实例永远不从方法中“逃脱”,换言之,它们永远不会存储到别的地方,
不会返回,也不会用于启动线程——那么事情就会简单得多。
从垃圾回收的角度,思考任何捕获变量被延长的生存期。这方面的问题一般都不大,但
假如捕获的对象会产生昂贵的内存开销,问题就会凸现出来。
第一条规则可奉为金科玉律。简化总是好事。所以在任何时候,如果使用一个捕获的变量
能使代码变得更简单(前提是你已将强迫代码维护人员理解捕获的变量所做的事情这一额外复
杂性考虑在内),那么就用它。但你也要考虑它所带来的额外的复杂度,不要一味地追求最少
的代码量。
列出了一些要记住的重要知识点,
1 捕获的是变量,而不是创建委托实例时它的值。
2 捕获的变量的生存期被延长了,至少和捕捉它的委托一样长。
3 多个委托可以捕获同一个变量……
4 ……但在循环内部,同一个变量声明实际上会引用不同的变量“实例”。
5 在 for 循环的声明中创建的变量
6 必要时创建额外的类型来保存捕获变量。
7 要小心!简单几乎总是比耍小聪明好。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了