理解c#中的闭包

无源低通滤波器 无源RC滤波器“滤除”不需要的信号,因为它们分离并允许仅通过其频率的正弦输入信号,最简单的是无源低通滤波器网络。在低频应用(高达100kHz)中,无源滤波器通常使用简单的RC(电阻-电容)网络构建,而高频滤波器(高于100kHz)通常由RLC(电阻-电感-电容)元件制成。无源滤波器由电阻器、电容器和电感器等无源元件组成,没有放大元件(晶体管、运算放大器等),因此没有信号增益,因此它们的输出电平始终小于输入。滤波器是根据它们允许通过它们的信号的频率范围来命名的,同时阻挡或“衰减”其余部分。 阅读详情

引用:

http://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); 
    } 
}
复制代码

 

 

 


COMSOL实战:薄膜声学超材料模态分析全流程解析(含预应力设置技巧) 本文详细解析了使用COMSOL进行薄膜声学超材料模态分析的全流程,包括预应力设置技巧和共振频率优化方法。通过实际案例和参数设置指南,帮助工程师掌握从几何建模到结果解读的关键步骤,特别适合声学超材料和共振薄膜结构的设计与优化。 阅读详情

相关推荐

T100服务端接口实战:从注册到日志排查的全链路开发指南

本文提供了一份T100服务端接口开发的全链路实战指南。从程序注册与框架签出开始,详细讲解了接口的数据结构设计、核心业务逻辑编写,到最终的部署上线与调用验证。文章重点分享了如何利用日志文件高效定位和排查接口问题,帮助开发者快速掌握T100接口开发的关键步骤与实用技巧。

weixin_28831399的博客 296

闭包与匿名函数

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

lfanyize的博客 554

10-142 6-4 查询厂商“D“生产的PC和便携式电脑的平均价格

分数 10全屏浏览题目切换布局作者 lhm单位 河北农业大学本题目要求编写SQL语句,查询厂商D生产的PC和便携式电脑的平均价格。

dqd66的博客 2748

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

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

菜鸡工坊 747

手把手教你用Arduino Uno控制meArm机械臂(附完整代码+避坑指南)

本文提供了一份详尽的meArm机械臂控制指南,涵盖从Arduino Uno硬件连接到Servo库编程的完整流程。重点讲解了如何通过独立供电、软件限位和平滑运动函数实现稳定控制,并设计了串口指令协议与高级功能,帮助创客有效规避舵机抖动、电源不足等常见问题,让机械臂运行更流畅可靠。

gpt4scribbler的博客 538

让Qt程序适配高分辨率屏幕,解决软件界面错乱异常

前期使用Qt开发一个登录模块一直使用很好。最近在一台新电脑上使用时发现排版混乱。这个问题很普遍,主要原因是旧软件对高清显示器的适配不当造成的。新电脑使用的是4K屏,win10系统自动进行了175%的缩放。

cihron的博客 1万+

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

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

weixin_30834783的博客 153

WGCNA分析中那些没人告诉你的坑:以肺结核差异基因模块筛选为例

本文深入探讨WGCNA分析在肺结核差异基因模块筛选中的七个关键陷阱,包括soft-thresholding选取、样本离群值检测、模块合并阈值调整等。通过实际案例和R代码示例,揭示生信数据挖掘中常被忽视的技术细节,帮助研究者避免常见错误,提升分析结果的可靠性和生物学意义。

weixin_29218509的博客 261

【C#】闭包

关于C#闭包的分析

m0_62389241的博客 2099

把 ABAP 出站 IDoc 做到稳如老狗:从数据抽取到端口投递的全链路拆解

SAP出站IDoc创建流程解析 本文详细拆解了SAP系统中出站IDoc的创建流程,将其归纳为四个关键阶段:数据获取、数据转换、控制记录校验和端口投递。文章指出,IDoc的稳定性取决于整条流水线的设计质量,而不仅仅是字段映射。 核心要点包括: IDoc由控制记录和数据记录组成,需在WE31/WE30中定义段结构 2.数据获取阶段需明确触发点、依赖链和一致性保证 3.数据转换应遵循"结构版本+映射纪律"原则 4.控制记录校验决定IDoc能否发出,需与WE20伙伴配置匹配 5.端口类型(tRF

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

18、项目运行与调试全攻略

本文详细介绍了在IntelliJ IDEA中进行项目运行与调试的完整流程,涵盖运行配置的创建与管理、临时配置的使用与保存、运行窗口的控制台操作及多程序并行运行的处理。同时,讲解了如何通过调试器定位和修复语法错误、运行时错误及逻辑错误,并结合ACME项目演示了第三方库Log4J的集成方法和命令行客户端的功能改进。文章还提供了调试流程图,帮助开发者系统掌握IDEA的强大调试功能,提升开发效率与代码质量。

svm4gardener的博客 75

闭包(C#)

通常来讲,大家一听到闭包,应该首先会想到JavaScript中的闭包,而不会想到C#中的闭包,但是C#中也是有闭包的,下面就让我来为大家仔细讲解讲解。 在C#中,我们通常知道变量作用域有三种:1、是属于类的,我们常称之为field,2、是属于函数的,我们通常称之为局部变量,3、其实也是属于函数的,不过它的作用范围更小,它只属于函数局部的代码片段,这种我们同样称之为局部变量。

草原上唱山歌的博客 1595

ESP-SPARKBOT AI 智能机器人:v1.2 全流程复刻指南

本文介绍 ESP-SPARKBOT AI 智能机器人 v1.2 版本的全流程复刻,包含 PCB 、3D 打印、固件烧录、常见问题解决、参考资料等

举世誉之而不加劝,举世非之而不加沮,定乎内外之分,辩乎荣辱之境,斯已矣。 9245

理解C#中的闭包

1、 闭包的含义首先闭包并不是针对某一特定语言的概念,而是一个通用的概念。除了在各个支持函数式编程的语言中,我们会接触到它。一些不支持函数式编程的语言中也能支持闭包(如java8之前的匿...

dotNET跨平台 638

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

通过IL理解c#闭包

通过IL理解c#闭包

xyorin的博客 230

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

C# 闭包的理解

C#闭包,JavaScript闭包。

qq_38192821的博客 566

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

正确使用和理解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的博客 514

从编译器层面理解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的专栏 2678

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

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

426

C#与闭包

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

liguo9860的专栏 814
上一篇: c# 闭包
下一篇: 针孔相机拍摄的图像坐标和空间点的对应关系
nodeathphoenix
博客等级 码龄16年 97粉丝 99原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值