详细聊聊如何在C#循环中捕获局部变量
作者:无欲则刚9862 发布时间:2021-10-30 17:05:13
标签:c#,捕获,局部变量
目录
问题:
解答方案:
总结
问题:
我遇到了一个有趣的问题,它的代码大概是这样的。
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
actions.Add(() => variable * 2);
++ variable;
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
我的期望输出是 0,2,4,6,8,但它最终输出的是五个 10,看起来像是这些 action 上下文捕获的都是同一个变量。
请问是否有变通的方法实现我要的预期结果呢?
解答方案:
变通方法就是在你的 loop 循环体中使用一个中间变量,并将其送入到 lambda 体中,参考如下代码:
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
int variable1 = variable;
actions.Add(() => variable1 * 2);
++variable;
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
Console.ReadLine();
其实这种情况在多线程下也同样会发生,比如下面的代码:
for (int counter = 1; counter <= 5; counter++)
{
new Thread (() => Console.Write (counter)).Start();
}
你以为会打印出 1,2,3,4,5,最后的结果非常有趣,在我这里的输出结果是:2,1,3,3,4 ,你那边的结果肯定又不一样了。
唯一的解决方案就是使用 局部变量,修改后的代码如下:
for (int counter = 1; counter <= 5; counter++)
{
int localVar= counter;
new Thread (() => Console.Write (localVar)).Start();
}
总结
可能有很多朋友不知道为什么加了一个 variable1 变量之后,代码就正确的,要想分析,可以看 C# 生成的 IL 代码。
private static void Main(string[] args)
{
List<Func<int>> actions = new List<Func<int>>();
for (int variable = 0; variable < 5; variable++)
{
<>c__DisplayClass0_0 <>c__DisplayClass0_ = new <>c__DisplayClass0_0();
<>c__DisplayClass0_.variable1 = variable;
actions.Add(new Func<int>(<>c__DisplayClass0_.<Main>b__0));
}
foreach (Func<int> act in actions)
{
Console.WriteLine(act());
}
Console.ReadLine();
}
可以清楚的看到,那个所谓的 variable1 成了匿名类 c__DisplayClass0_0 下的字段了,而且foreach循环中每次都是new的,所以这个字段必然不一样,这也就保证了正确的结果。
来源:https://blog.csdn.net/qq_41872328/article/details/120301266


猜你喜欢
- 微服务通过feign.RequestInterceptor传递参数Feign 支持请求 * ,在发送请求前,可以对发送的模板进行操作,例如设
- 今年新开Java课程第一步就是…配置环境就从Java的环境配置开始好了以下是正式的步骤首先,从Oracle的官网下载jdk的安装包点我下载J
- java8 Stream大数据量List分批处理//按每3个一组分割private static final Integer MAX_NUM
- 在spring上传文件中,一般都使用了MultipartFile来接收,但是有需要用到File的地方,这里只介绍两种转为File的方法,当然
- java随机验证码生成实现实例代码摘要: 在项目中有很多情况下都需要使用到随机验证码,这里提供一个java的随机验证码生成方案,可以指定难度
- Android深入浅出之Binder机制一 说明 Android系统最常见也是初学者最难搞明白的就是Binder了,很多很多的Se
- @schedule注解动态配置时间间隔动态配置时间间隔是通过自己实现的任务注册到任务调度实现的,并在每次调度的时候更改下次调度时间间隔,如果
- 今天在开发的过程中,遇到java.lang.ExceptionInInitializerError异常,百度查了一下,顺便学习学习,做个笔记
- 假如我们有一张banner_item表,现需要通过banner_id查出所有数据(查询List)@Datapublic class Bann
- 什么是Java垃圾回收器Java垃圾回收器是Java虚拟机(JVM)的三个重要模块(另外两个是解释器和多线程机制)之一,为应用程序提供内存的
- 在Mms中每个Thread都有其相应的联系人,但是threads表中并没有直接保存联系人的信息(号码或名字),而是保存一个叫做recipie
- 我想,对于各位使用面向对象编程语言的程序员来说,“接口”这个名词一定不陌生,但是不知各位有没有这样的疑惑:接口有什么用途?它和抽象类有什么区
- Springboot添加server.servlet.context-pathserver.servlet.context-path配置的作
- 自定义Starter命名规则注意artifactId的命名规则,Spring官方Starter通常命名为spring-boot-starte
- Java的源代码是以*.java的纯文本文件,可以使用任何文本编辑器来进行编写,但是这个源代码是无法执行的。执行源代码的这个任务就需要JDK
- 本文实例讲述了Java线程同步方法。分享给大家供大家参考,具体如下:1. Semaphore1.1 二进制SemaphoreSemaphor
- 如何解析PDF文件在.NET中从PDF文件里提取文本的几种主要方法有:1、Microsoft 的 IFilter 接口 和 Adobe 的
- equals函数在基类object中已经定义,源码如下 public boolean equals(Object obj) { return
- 启动Springboot项目时候报错java: 无法访问org.springframework.boot.SpringApplication
- 在C#中通过使用方法来获取返回值时,通常只能得到一个返回值。因此,当一个方法需要返回多个值的时候,就需要用到ref和out,那么这两个方法区