理解C#中的闭包

caxa二次开发实例展示 本人有8年CAXA各产品(如电子图版、工艺图表、PDM等)二次开发经验,参与过国家电网南瑞集团、中石油、龙记集团、欧姆龙集团、港日集团、中集集团、中国五矿、方圆塑机、大连锅炉、金蝶PDM集成CAXA,TEAMCENTER集成CAXA、大连锅炉机械、中国中车等项目的开发,擅长CAXA电子图版、CAXA工艺图表的二次开发,有需要的可以联系我,电话:13169467685、13249738576 Q 阅读详情

转发收藏

原文链接https://www.cnblogs.com/jiejie_peng/p/3701070.html


闭包的概念

内层的函数可以引用包含在它外层的函数的变量,即使外层函数的执行已经终止。但该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值。

闭包的优点

使用闭包,我们可以轻松的访问外层函数定义的变量,这在匿名方法中普遍使用。比如有如下场景,在winform应用程序中,我们希望做这么一个效果,当用户关闭窗体时,给用户一个提示框。我们将添加如下代码:

复制代码
private void Form1_Load(object sender, EventArgs e)
{
       string tipWords = "您将关闭当前对话框";
       this.FormClosing += delegate
       {
            MessageBox.Show(tipWords);
       };
}
复制代码

若不使用匿名函数,我们就需要使用其他方式来将tipWords变量的值传递给FormClosing注册的处理函数,这就增加了不必要的工作量。

闭包陷阱

应用闭包,我们要注意一个陷阱。比如有一个用户信息的数组,我们需要遍历每一个用户,对各个用户做处理后输出用户名。

复制代码
public class UserModel
{
        public string UserName
        {
            get;
            set;
        }

        public int UserAge
        {
            get;
            set;
        }
}
复制代码

 

复制代码
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++)
            {
                ThreadPool.QueueUserWorkItem((obj) =>
                {
                    //TODO
                    //Do some process...
                    //...
                    Thread.Sleep(1000);
                    UserModel u = userList[i];
                    Console.WriteLine(u.UserName);
                });
            }
复制代码

 

 

 

我们预期的输出是, 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);
                });
            }
复制代码

 

 

 

我们再运行来看,输出依次为 jiejiep,xiaoyi, zhangzetian.注意,每次的输出顺序可能不同。

NET编译器与闭包

提出了问题,给出了解决方案,我们总算知道该怎么正确使用闭包了。但是dotNET是如何实现闭包的呢?执着的程序猿们,不会满足于这种表象的解决方案,让我们来看看dotNET是如何实现闭包的。我们可以微软提供的isdasm.exe来查看编译后的代码。我们先来看看有问题的代码。将IL代码翻译后,可以得到如下的伪代码。

 

复制代码
public  class TempClass5
{
    public List<UserModel> UserList;

}


public class TempClass8
{

    public int i = 0;
    
    
    public TempClass5 c5;
    
    public ShowMessage(object o)
    {
        
        Thread.Sleep(1000);
        
        Console.WriteLine(c5.UserList[i].UserName);
    }
}
复制代码

 

复制代码
public class Program
{
    TempClass5 c55 = new TempClass5();
    c55.UserList = new List<UserModel>();
    
    c55.UserList.Add(new UserModel{ UserName="jiejiep", UserAge = 26});
    c55.UserList.Add(new UserModel{ UserName="xiaoyi", UserAge = 25});
    c55.UserList.Add(new UserModel{ UserName="zhangzetian", UserAge=24});
    
    TempClass8 c8 = new TempClass8();
    c8.c5 = c55;
    
    WaitCallback callback = c8.ShowMessage;
    
    for(int c8.i=0; c8.i < 3; c8.i++)
    {
        ThreadPool.QueueUserWorkItem(callback);
    }
}
复制代码

 

 

原来,编译器为我们生成了一个临时类,该类包含一个 int成员i,一个TempClass5实例c5, 一个实例方法 ShowMessage(object) 。再看看遍历部分的代码,我们顿时就豁然开朗了,原来一直都只有一个 TempClass8实例,遍历时始终改变的是tempCls对象的i字段的值。所以最后输出的,始终是最后一个遍历得到的元素的 UserName 。

我们再来看看使用临时变量后的代码,编译器是如何处理的呢。

 

复制代码
public class Program
{
    TempClass5 c55 = new TempClass5();
    c55.UserList = new List<UserModel>();
    
    c55.UserList.Add(new UserModel{ UserName="jiejiep", UserAge = 26});
    c55.UserList.Add(new UserModel{ UserName="xiaoyi", UserAge = 25});
    c55.UserList.Add(new UserModel{ UserName="zhangzetian", UserAge=24});
    
    for(int i=0; i < 3; i++)
    {
        TempClass8 c8 = new TempClass8();
        c8.c5 = c55;
        c8.i = i;
        WaitCallback callback = c8.ShowMessage;
        ThreadPool.QueueUserWorkItem(callback); 
    } 
}
复制代码

 

 

 

我们看到,使用临时变量这种解决方案时,编译器相当于是每次遍历时都实例化了一个 TempClass8对象。所以内层函数引用的c8的i成员始终是遍历对应的元素。故能有效的解决闭包带来的陷阱。

刷机维修进阶教程-----adb禁用错了系统app导致无法开机 如何保数据无损恢复机型 在刷机维修过程中 。我们会遇到一些由于客户使用adb指令来禁用手机app而导致手机无法开机进入系统的故障机型。通常此类问题机型有好几种解决方法。但如果客户需要保数据来恢复机型。其实操作也是很简单的.还有类似误删除应用导致不开机等等如何保数据。  通过博文了解💝💝💝    1💝💝💝----adb禁用或者删除系统app导致无法开机的几种解决方法 2💝💝💝----如何保数据来无损恢复机型 3💝💝💝----其他保数据解决系统类故障无法开机的步骤解析 阅读详情

相关推荐

保姆级教程:在Ubuntu 20.04上从源码编译Apollo自动驾驶平台(含GPU环境配置)

本文提供了一份详细的Ubuntu 20.04上从源码编译Apollo自动驾驶平台的保姆级教程,包括GPU环境配置、Docker部署和编译构建等关键步骤。通过实战经验和避坑指南,帮助开发者高效完成Apollo平台的编译和验证,适用于自动驾驶技术研究和应用开发。

weixin_30876945的博客 409

理解c#中的闭包

引用: http://www.cnblogs.com/jiejie_peng/p/3701070.html 闭包的概念 内层的函数可以引用包含在它外层的函数的变量,即使外层函数的执行已经终止。但该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值。 闭包的优点 使用闭包,我们可以轻松的访问外层函数定义的变量,这在匿名方法中普遍使用。比如有如下场景,在winform

nodeathphoenix的专栏 831

SAP ERP销售应收&成本科目确定

ERP作为业财一体软件,其中最核心的就是业务与财务模块的自动集成,无需财务进行手工做账,提高工作效率的同时降低人工操作产生的误差。今天浅浅的分享一下SD模块生成会计凭证时,借贷项确定的系统配置。一般项目上借贷项、科目都有FICO提供,但是具体的集成配置是在SD这边。

weixin_43657667的博客 1569

闭包与匿名函数

什么是闭包? 所谓的闭包指的是:一个方法除了能和传递给他的参数交互之外,还可以和上下文进行更大成都的互动,但是闭包并不是C#语言独有的,事实上,这是一个很早的概念,而且现在很多驻留的编程语言都接纳了这个概念,如果要真正理解闭包要理解两个概念 1.外部变量:或者称为匿名方法的外部变量,指的是定义了一个匿名方法的作用域内(方法内部的)局部变量或者参数,对匿名方法来说是外部变量 2.捕获的外部变量:即在匿名方法内部使用的外部变量,也就是局部变量在匿名方法内部便是一个捕获的外部变量 知道这两种概念之后,再结合闭包的

lfanyize的博客 554

Proxmox VE网络配置踩坑实录:从Web界面无法访问到完美修复的全过程

本文详细记录了Proxmox VE服务器Web界面无法访问的完整排查与修复过程。作者从基础网络连通性测试入手,逐步排查了服务状态、SSL证书、端口监听、防火墙规则及网关配置,最终定位并解决了问题。文章提供了系统性的故障排查思路和实用的修复命令,对管理Proxmox VE网络具有重要参考价值。

weixin_29231221的博客 890

双有源桥DC-DC变换器:原理、软开关与移相控制深度解析

隔离型DC-DC变换器是电力电子系统的核心部件,通过高频变压器实现电气隔离与电压匹配,保障系统安全与灵活适配。其工作原理基于功率半导体器件的开关动作,通过调制策略控制能量传输。该技术的核心价值在于实现高效、高功率密度的电能转换,并支持双向能量流动,这对于提升系统整体能效和运行灵活性至关重要。在新能源储能、电动汽车V2G及数据中心供电等场景中,双向能量流成为刚性需求,推动了如双有源桥等先进拓扑的应用。本文聚焦于双有源桥这一实现高效双向隔离变换的“明星”方案,深入剖析其利用移相控制实现软开关的机理。通过分析谐振

weixin_34195142的博客 380

Javascript闭包和C#匿名函数对比分析

C#中引入匿名函数,多少都是受到Javascript的闭包语法和面向函数编程语言的影响。人们发现,在表达式中直接编写函数代码是一种普遍存在的需求,这种语法将比那种必须在某个特定地方定义函数的方式灵活和高效很多,比如回调和事件处理都特别适合使用表达式中直接编写函数的形式,因此C#的匿名函数也就应运而生。 初识C#中的匿名函数,多多少少并不是那么直观,在匿名函数中,可以直接使用该匿名函数所在的函数中...

weixin_30834783的博客 153

TriCore架构-基于TC397在SOTA过程中AB-SWAP切换底层原理

TC397的SOTA升级机制通过硬件地址重映射实现,利用MMU将逻辑地址动态映射到不同物理地址。其PFlash支持标准/备用地址映射,通常使用PF0+PF1+PF4空间以便于OTA处理。升级时,新固件自动写入非活动分区(A/B分区切换),由地址转换函数完成逻辑地址到物理地址的转换。系统重启后根据寄存器选择启动区域,实现安全无缝的固件更新。

xiandang8023的博客 490

C#中的委托 匿名函数 闭包

委托 委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If … Else(Switch)语句,同时使得程序具有更好的可扩展性。 public delegate void GreetingDelegate(string name); class Program { private static voi...

菜鸡工坊 747

Fastgpt本地使用Docker Compose 快速部署

修改文件,并执行再执行d重起容器。具体配置,参考配置详解。可以查看日志,在启动容器后,第一次请求网页,会进行配置文件读取,可以看看有没有读取成功以及有无错误日志。进入 FastGPT 容器,可以通过ls data查看目录下是否成功挂载文件。可通过查看配置文件。可能不生效的原因挂载目录不正确配置文件不正确,日志中会提示invalid json,配置文件需要是标准的 JSON 文件。

洛阳泰山的博客 1634

Central Finance 全景解析:用 SAP S/4HANA 构建跨系统的财务中台与渐进式转型路径

Central Finance 是基于 SAP S/4HANA 的一种部署与应用场景:在不打断现有异构源系统运营的前提下,把多套 SAP 与非 SAP 系统中的财务与管理会计数据实时/准实时复制到一套中心化的 SAP S/4HANA 中,作为统一的记账与分析底座,逐步把选定的财务流程(如预算控制、付款、固定资产结账等)迁入中心系统。这让企业既能快速获得统一报表与统一核算结构,又为未来的整体迁移留出了弹性路径。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 393

【C#】闭包

关于C#闭包的分析

m0_62389241的博客 2100

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

通过IL理解c#闭包

通过IL理解c#闭包

xyorin的博客 230

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

C# 闭包的理解

C#闭包,JavaScript闭包。

qq_38192821的博客 566

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

正确使用和理解C#中的闭包

本文介绍了C#中闭包和捕获变量的概念及相关特性。文章首先定义了捕获变量和闭包的概念,然后通过代码示例展示了捕获变量在委托调用时使用最新值的特性。重点分析了for循环迭代变量作为捕获变量时的行为差异,以及使用局部变量和foreach循环的不同表现。文章还从IL代码层面解释了其实现原理,指出编译器会生成一个密封类来包装捕获变量和方法。特别说明了从C#5.0开始,foreach循环变量行为的变化。通过这些示例和原理分析,帮助读者深入理解闭包和捕获变量的工作机制。

liweixxxx1990的专栏 551

[C#学习笔记] - C#中闭包的一些理解

持续更新中… 看过一些文章,总结下来就是,闭包是一个带共享数据的函数,嵌套定义函数,使用了外部定义域(非全局定义域)的变量。是否有返回值并不影响判断。 示例: 这三个函数几乎一样,唯一不同的就是action函数里变量。 函数action => Console.WriteLine(x)关联的变量是x。这个匿名函数以该形式保存。直至被调用时,才会去取对应的i。 private static v...

kyw的专栏 309

C#闭包的原理理解

分别对应生成的代码如下:注意红色位置的区别,所以第一个输出:5,5,5,5,5;第二个输出0,1,2,3,4。第一种只会生成一个类实例(这个类是C#编译器帮我们自动生成的,匿名函数又使用了局部变量时),第二种会生成5个类的实例。[在这里插入图片描述](https://img-blog.csdnimg.cn/1b703eca284744999b64b50a3baa9322.jpeg。生成的代码:只会生成一个类的静态委托,这样可以节约性能,不用new新的对象了,只用new一次委托对象。

kylinok的博客 404

简单理解C#中的闭包(不涉及原理

理解的定义:一个函数内部使用了一个外部变量,在支持闭包的语言里,这个外部变量实际上会随着函数的存在而存在,这个函数和外部变量就一起形成了闭包。 public void T() { var n = 0; Console.WriteLine(++n); } 比如在这个T函数中,有个局部变量n,T函数执行完成后n也随着消失了。 现在改成这样写,T1返回了一个匿名函数,这个函数依赖...

qq_36581112的博客 515

从编译器层面理解C#中的闭包的这个坑!

前言在公众号上看到一篇文章《正确使用和理解C#中的闭包》,里面提到了闭包的一个坑:当捕获的外部变量为for循环的迭代变量时,C#认为变量i是定义在循环体外的。所以,当添加委托集合的for循...

dotNET跨平台 346

C#深入理解闭包问题

首先认识一下什么是闭包:内层函数引用外层函数的变量的最终值(产生的原因就是作用域) 注:一个大括号就是一个作用域 先来看一段代码: Action b = null; for (int i = 0; i < 5; i++) {//作用域一 b += () => {//作用域二 Console.WriteLine(i); }; } b(); 输出结果 5 5 5 5 5 分析: 作用域一就是外层函数,作用域二是内层函数 ①在作用域一中,i只被声明了1次 ②i的最终值是5 ③作用域二中引用

qq_51214471的博客 511

C#:深入理解 lambda表达式与闭包

1. Lambda表达式 1.1 简述Lambda表达式 Lambda表达式实际上是简化委托的写法,只要有委托参数类型的地方,就可以使用Lambda表达式表示。 为了比较委托写法与Lambda表达式的差异,实现两个int类型相加的委托进行对比: 委托写法: Action<int, int> act = Add; ​ public static void Add(int a,int b) { Console.WriteLine(a + b); } Lambda表达式: A

YoungProgrammer的专栏 2680

浅谈 .NET 中的对象引用、非托管指针和托管指针 理解C#中的闭包

浅谈 .NET 中的对象引用、非托管指针和托管指针 目录 前言 一、对象引用 二、值传递和引用传递 三、初识托管指针和非托管指针 四、非托管指针 1、非托管指针不能指向对象引用 2、类成员指针 五、托管指针 前言 本文主要是以 C# 为例介绍 .NET 中的三种指针类型(本文不包含对于函数指针的介绍):对象引用、非托...

427

C#与闭包

首先想说明一点,虽然有这样那样的不好的心态(比如中文技术书),但总体来说,国内的技术人员还是喜欢分享和教导别人的,这点我的个人感受和之前在园子里看到的朋友的感受恰恰相反。个人认为其实国内很多技术网友都是很热心的,可能因为语言问题同一个技术热点会稍稍落后国外一些,但一些成熟的或者基础的概念都可以找到很细致的中文介绍,特别是关于闭包。因为它的字面解释确实很绕,所以基本所有试图解释这一名词的同学都是尽量

liguo9860的专栏 814

C#_闭包

C#_闭包 一、闭包的含义 首先,闭包并不是针对某一特定语言的概念,而是一个通用的概念。 闭包是指有权访问另一个函数作用域中的变量的函数。注意,闭包这个词本身值得是一种函数。而创建这种特殊函数的一种常见方式是在一个函数中创建另一个函数。在C#中我们可以使用委托或者本地函数。 二、理解闭包的实现原理 static void Main(string[] args) { GetAction()(30); } static A

目标:MVP 4055
上一篇: Unity3d 简单的小球沿贝塞尔曲线运动(适合场景漫游使用)
下一篇: SVN代码冲突解决方案
深林_
博客等级 码龄10年 22粉丝 26原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值