闭包(C#)

通常来讲,大家一听到闭包,应该首先会想到JavaScript中的闭包,而不会想到C#中的闭包,但是C#中也是有闭包的,下面就让我来为大家仔细讲解讲解。

在C#中,我们通常知道变量作用域有三种:1、是属于类的,我们常称之为field,2、是属于函数的,我们通常称之为局部变量,3、其实也是属于函数的,不过它的作用范围更小,它只属于函数局部的代码片段,这种我们同样称之为局部变量。

这三种变量的生命周期都属于它所寄存的对象,变量是随着寄存对象的销毁而消亡。

三种作用域可以这样理解:类中的变量是随着类实例化而产生,同时伴随着类对象资源回收而消亡,类中的static和const对象除外;函数的变量随着函数的调用开始而产生,伴随着函数执行结束而结束,函数内部的变量生命周期满足先进后出的特点;

闭包是使用的变量已经脱离其作用域,由于变量和作用域之间存在上下文关系,从而可以在当前环境中继续使用,即上下文环境所定义的一种函数对象。

C#中,闭包允许你将一些行为进行封装,将这个封装的行为当成对象进行传递,但是它能够访问到最初声明时的上下文。

首先我们看一个最简单的javascript中经常见到的关于闭包的例子:

function f1(){var n=999;return function(){alert(n); // 999return n;}
}var a =f1();alert(a()); 

将上面的Java Script代码翻译成C#代码如下:

public class TCloser{public Func<int> T1(){var n = 999;return () =>{Console.WriteLine(n);return n;};}}class Program{static void Main(){var a =new TCloser();var b = a.T1();Console.WriteLine(b());}} 

从上面的代码可以看出,变量n是属于函数T1的局部变量,n的生命周期应该是伴随着函数T1的调用结束而结束,但却在返回的委托b中仍能够调用,这正是C#闭包所展示出的威力,其实闭包就相当于委托。

当T1调用返回的匿名委托时,编译器会判断这是合法的,返回的委托b和函数T1存在上下文关系,匿名委托是允许使用它所在的函数或类中的局部变量,从而编译器通过一系列动作使调用的函数T1的局部变量自动闭合,该局部变量满足新的作用范围。

讨论一下C#中的闭包

1、静态全局字段

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace ConsoleApplication3
{class Program{public static int copy;//[0]这个不是闭包static void Main(){//定义动作组List<Action> actions = new List<Action>();for (int counter = 0; counter < 10; counter++){copy = counter;actions.Add(() => Console.WriteLine(copy));}//执行动作foreach (Action action in actions) action();}}
}//注:Action定义如下:
//public delegate void Action();

2、局部变量(闭包一)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace ConsoleApplication3
{class Program{static void Main(){int copy;//[1]闭包一//定义动作组List<Action> actions = new List<Action>();for (int counter = 0; counter < 10; counter++){copy = counter;actions.Add(() => Console.WriteLine(copy));}//执行动作foreach (Action action in actions) action();}}
}//注:Action定义如下:
//public delegate void Action();

3、局部变量(闭包二)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace ConsoleApplication3
{class Program{static void Main(){//定义动作组List<Action> actions = new List<Action>();for (int counter = 0; counter < 10; counter++){int copy;//[1]闭包二copy = counter;//int copy = counter;//换种写法actions.Add(() => Console.WriteLine(copy));}//执行动作foreach (Action action in actions) action();}}
}//注:Action定义如下:
//public delegate void Action();

4、局部变量(闭包三)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace ConsoleApplication3
{class Program{static void Main(){//定义动作组List<Action> actions = new List<Action>();for (int counter = 0; counter < 10; counter++)//[3]闭包三{actions.Add(() => Console.WriteLine(counter));}//执行动作foreach (Action action in actions) action();}}
}//注:Action定义如下:
//public delegate void Action();

1:输出什么?
2:输出什么?
3:输出什么?
4:输出什么?

这几个例子,可以将匿名函数进行转换,这样可以看的更清楚。

在1中,“外部变量”copy是类的一个静态成员,因此可以讲匿名函数转换为以下形式:

class Program{public static int copy;//[0]这个不是闭包static void TempMethod(){Console.WriteLine(copy);}static void Main(){//定义动作组List<Action> actions = new List<Action>();for (int counter = 0; counter < 10; counter++){copy = counter;actions.Add(new Action(TempMethod));}//执行动作foreach (Action action in actions) action();}}

2和3中“外部变量”copy是Main方法中的局部变量,局部变量的生存期现在必须至少延长为匿名函数委托的生存期。这可以通过将局部变量“提升”到编译器生成的类的字段来实现。

之后,局部变量的实例化对应于为编译器生成的类创建实例,而访问局部变量则对应于访问编译器生成的类的实例中的字段。而且,匿名函数将会成为编译器生成类的实例方法:

class Program{static void Main(){//定义动作组TempClass tc = new TempClass();//定义动作组List<Action> actions = new List<Action>();for (int counter = 0; counter < 10; counter++){tc.copy = counter;actions.Add(tc.TempMethod);}//执行动作foreach (Action action in actions) action();}class TempClass{public int copy;public void TempMethod(){Console.WriteLine(copy);}}}
class Program{static void Main(){//定义动作组//定义动作组List<Action> actions = new List<Action>();for (int counter = 0; counter < 10; counter++){TempClass tc = new TempClass();tc.copy = counter;actions.Add(tc.TempMethod);}//执行动作foreach (Action action in actions) action();}class TempClass{public int copy;public void TempMethod(){Console.WriteLine(copy);}}}

4中的“外部变量”counter是for循环的循环因子,因此可以转换为以下形式:

class Program{static void Main(){//定义动作组List<Action> actions = new List<Action>();TempClass tc = new TempClass();for (tc.copy = 0; tc.copy < 10; tc.copy++){actions.Add(new Action(tc.TempMethod));}//执行动作foreach (Action action in actions) action();}class TempClass{public int copy;public void TempMethod(){Console.WriteLine(copy);}}}

闭包就先讲解这么多,下次在更加深入的讲解,欢迎各位宝宝们留言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/101376.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

docker compose和consul(服务注册与发现)

一、Docker-compose 简介 Docker-Compose项目是基于Python开发的Docker官方开源项目&#xff0c;负责实现对Docker容器集群的快速编排。 Docker-Compose将所管理的容器分为三层&#xff0c;分别是 工程&#xff08;project&#xff09;&#xff0c;服务&#xff08;service&a…

登录页面怎么做渗透

1.用万能密码登录&#xff0c;看是否能登录成功 ‘ or 11# 2.注入漏洞 用burp抓包放在sqlmap里面跑&#xff0c;看是否有注入 3.查看是否有说明文档找文件下载漏洞 一般的链接形式 download.php?path download.php?filename down.php?file data.php?file 包含的参数 &…

Mabatis-puls强于Mybatis的地方

Mabatis-puls与Mybatis都是优秀的Java持久化框架&#xff0c;但是Mabatis-puls相较于Mybatis有以下几个方面的优势&#xff1a; 性能更优&#xff1a;Mabatis-puls采用了Javassist技术&#xff0c;使得它在运行时比Mybatis更快速&#xff0c;尤其是在执行大量SQL的情况下&#…

TensorFlow入门(二十一、softmax算法与损失函数)

在实际使用softmax计算loss时,有一些关键地方与具体用法需要注意: 交叉熵是十分常用的,且在TensorFlow中被封装成了多个版本。多版本中,有的公式里直接带了交叉熵,有的需要自己单独手写公式求出。如果区分不清楚,在构建模型时,一旦出现问题将很难分析是模型的问题还是交叉熵的使…

代码随想录算法训练营第23期day19| 654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树

目录 一、&#xff08;leetcode 654&#xff09;最大二叉树 二、&#xff08;leetcode 617&#xff09;合并二叉树 三、&#xff08;leetcode 700&#xff09;二叉搜索树中的搜索 四、&#xff08;leetcode 98&#xff09;验证二叉搜索树 一、&#xff08;leetcode 654&…

Vega Prime入门教程14.01:调用VAPS XT DLL

本文首发于&#xff1a;Vega Prime入门教程14.01&#xff1a;调用VAPS XT DLL 在VAPS XT系列教程中提到过Vega Prime可以直接调用Drawing Integration生成的dll&#xff0c;本文来测试这个功能效果。 本系列使用的是VP18.0&#xff0c;使用的是VC14.0&#xff08;VS2015&…

Android Studio修改模拟器AVD Manger目录

Android Studio修改虚拟机AVD Manger目录 1、在AS的设备管理器Device Manager中删除原来创建的所有虚拟机&#xff08;Android Virtual Device&#xff09;&#xff1b; 2、新建一个自定义的AVD目录&#xff0c;例如&#xff1a;D:\Android\AndroidAVD 3、在高级系统设置中增加…

hive建表指定列分隔符为多字符分隔符实战(默认只支持单字符)

1、背景&#xff1a; 后端日志采集完成&#xff0c;清洗入hive表的过程中&#xff0c;发现字段之间的单一字符的分割符号已经不能满足列分割需求&#xff0c;因为字段值本身可能包含分隔符。所以列分隔符使用多个字符列分隔符迫在眉睫。 hive在建表时&#xff0c;通常使用ROW …

【Zookeeper专题】Zookeeper选举Leader源码解析

目录 前言阅读建议课程内容一、ZK Leader选举流程回顾二、源码流程图三、Leader选举模型图 学习总结 前言 为什么要看源码&#xff1f;说实在博主之前看Spring源码之前没想过这个问题。因为我在看之前就曾听闻大佬们说过【JavaCoder三板斧&#xff1a;Java&#xff0c;Mysql&a…

Arduino驱动LIS2DH三轴加速度传感器(惯性测量传感器篇)

目录 1、传感器特性 2、硬件原理图 3、控制器和传感器连线图 4、驱动程序 LIS2DH加速度计相对传统的ADXL345在稳定性以及功耗上都有一定的优化,低功耗模式下仅为2μA(普通模式11μA),并且最高支持5.3KHz输出频率,拥有2g/4g/8g/16g四档可选量程&

Kafka和RabbitMQ的对比

Rabbitmq比kafka可靠&#xff0c;kafka更适合IO高吞吐的处理&#xff0c;比如ELK日志收集 Kafka和RabbitMq一样是通用意图消息代理&#xff0c;他们都是以分布式部署为目的。但是他们对消息语义模型的定义的假设是非常不同的。 a) 以下场景比较适合使用Kafka。如果有大量的事…

程序员不写注释的原因及解决方案

目录 一、原因说明 二、问题分析 三、解决方案 在软件开发领域&#xff0c;注释的重要性不言而喻。它们不仅可以提高代码的可读性&#xff0c;还能帮助其他开发者更好地理解代码逻辑。然而&#xff0c;我们常常发现许多程序员在编写代码时并不喜欢添加注释。那么&#xff0c…

MAC 版PowerPoint 插入latex数学公式

参考链接&#xff1a; [IguanaTex Mac] PPT 中快捷导入LaTeX公式 - 知乎

Linux shell编程学习笔记9:字符串运算 和 if语句

Linux Shell 脚本编程和其他编程语言一样&#xff0c;支持算数、关系、布尔、字符串、文件测试等多种运算&#xff0c;同样也需要进行根据条件进行流程控制&#xff0c;提供了if、for、while、until等语句。 上期学习笔记中我们研究了字符串数据的使用&#xff0c;今天我们研…

cf 1886A

题目是输入一个数字&#xff0c;分解成三个数字的和&#xff0c;这三个数字都不相同&#xff0c;并且都不可以被三整除&#xff0c;如果存在输出YES并且输出任意一组可能的三个数字&#xff0c;否则输出NO 代码 #include<bits/stdc.h> using namespace std;int main() …

【Leetcode】202.快乐数

一、题目 1、题目描述 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为: 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果这个过程 结果为 1,那么这个数就是快乐…

Python:温度转换(摄氏度与华氏度)

Python:温度转换&#xff08;摄氏度与华氏度&#xff09; TempStrinput("请输入带有符号的温度值&#xff1a;") if TempStr[-1] in [F,f]:C(eval(TempStr[0:-1])-32)/1.8print("转换后的温度是{:.2f}C".format(C)) elif TempStr[-1] in[C,c]:F1.8*eval(T…

定时任务Apscheduler实践案例

定时任务Apscheduler实践案例 参考文章 https://blog.csdn.net/weixin_44799217/article/details/127353134 实现案例 本案例是使用定时任务apscheduler实现的每个三分钟发送一次邮件的任务 实现代码 import time from apscheduler.schedulers.blocking import BlockingSched…

设备管理工具

做了一个代理类,抽象出来后在注册表中&#xff0c;查找已经注册的设备 python 中 dict 和 lua 中的 table 一样高效 先初始化找到的设备通信程序,底层接口准备好C,这个设备调试界面就是可以用的,剩下就是MV了 软件升级已经稳定可用了 包括软件的备份和回滚操作登录时为设备页面…

持续集成交付CICD:Jenkins部署

目录 一、理论 1.CI/CD 2.Gitlab内置持续集成 3.Jenkins安装与部署 4.Gitlab服务部署 5.Jenkins服务部署 6.Tomcat服务部署 7.Jenkins–Pipeline流水线项目构建 二、实验 1.Gitlab服务部署 2.Jenkins服务部署 3.Tomcat服务部署 4.Jenkins–Pipeline流水线项目构建…