当前位置: 首页 > news >正文

C# 中的闭包

文章目录

  • 前言
  • 一、闭包的基本概念
  • 二、匿名函数中的闭包
    • 1、定义和使用匿名函数
    • 2、匿名函数捕获外部变量
    • 3、闭包的生命周期
  • 三、Lambda 表达式中的闭包
    • 1、定义和使用 Lambda 表达式
    • 2、Lambda 表达式捕获外部变量
    • 3、闭包的作用域
  • 四、闭包的应用场景
    • 1、事件处理
    • 2、异步编程
    • 3、迭代器和 LINQ 查询
  • 五、闭包的注意事项
    • 1、变量捕获的副作用
    • 2、闭包的性能影响
    • 3、闭包的内存管理
  • 六、总结


前言

  在 C# 中,闭包是一个强大的概念,它允许函数捕获外部变量并在函数外部访问这些变量。闭包在很多场景下都非常有用,比如在匿名函数、Lambda 表达式和委托中。本教程将详细介绍 C# 中的闭包。
在这里插入图片描述

一、闭包的基本概念

  闭包是一种将函数与其周围的环境(即外部变量)封装在一起的技术。在 C# 中,闭包通常是通过匿名函数或 Lambda 表达式实现的。当一个匿名函数或 Lambda 表达式引用了外部变量时,这个变量就被 “捕获” 到了闭包中,并且可以在函数内部访问和修改。

二、匿名函数中的闭包

1、定义和使用匿名函数

delegate int AnonFunc();
AnonFunc func = delegate()
{return 10;
};

  匿名函数是一种没有名称的函数,它可以在代码中直接定义并使用。在 C# 中,匿名函数通常使用delegate关键字或 Lambda 表达式来定义。例如:
  在这个例子中,我们定义了一个匿名函数,并将其赋值给一个委托变量。这个匿名函数没有参数,并且返回值为 10。

2、匿名函数捕获外部变量

  匿名函数可以捕获外部变量,并在函数内部访问和修改这些变量。例如:

int x = 5;
delegate int AnonFunc();
AnonFunc func = delegate()
{return x;
};

  在这个例子中,匿名函数捕获了外部变量x,并在函数内部返回了这个变量的值。

3、闭包的生命周期

delegate int AnonFunc();
int x = 5;
AnonFunc func = delegate()
{return x;
};
x = 10;
Console.WriteLine(func());

  闭包的生命周期与捕获它的委托或 Lambda 表达式的生命周期相同。这意味着,只要委托或 Lambda 表达式存在,闭包就存在,并且可以访问和修改捕获的变量。例如:
  在这个例子中,我们首先定义了一个匿名函数,它捕获了外部变量x。然后,我们修改了变量x的值,并调用了匿名函数。由于闭包的存在,匿名函数返回了修改后的变量x的值。

三、Lambda 表达式中的闭包

1、定义和使用 Lambda 表达式

  Lambda 表达式是一种简洁的匿名函数语法,它可以在代码中直接定义并使用。在 C#中,Lambda 表达式通常使用=>运算符来定义。例如:

   Func<int> func = () => 10;

  在这个例子中,我们定义了一个 Lambda 表达式,并将其赋值给一个委托变量。这个 Lambda 表达式没有参数,并且返回值为 10。

2、Lambda 表达式捕获外部变量

int x = 5;
Func<int> func = () => x;

  Lambda 表达式可以捕获外部变量,并在函数内部访问和修改这些变量。例如:
  在这个例子中,Lambda 表达式捕获了外部变量x,并在函数内部返回了这个变量的值。

3、闭包的作用域

  闭包中的变量的作用域与捕获它的 Lambda 表达式的作用域相同。这意味着,只要 Lambda 表达式存在,闭包就存在,并且可以访问和修改捕获的变量。例如:

int x = 5;Func<int> func = () => x;x = 10;Console.WriteLine(func());

  在这个例子中,我们首先定义了一个 Lambda 表达式,它捕获了外部变量x。然后,我们修改了变量x的值,并调用了 Lambda 表达式。由于闭包的存在,Lambda 表达式返回了修改后的变量x的值。

四、闭包的应用场景

1、事件处理

Button button = new Button();
int count = 0;
button.Click += (sender, e) =>
{count++;Console.WriteLine($"Button clicked {count} times.");
};

  闭包在事件处理中非常有用,因为它可以捕获事件发生时的上下文信息。例如,在 WPF 或 WinForms 应用程序中,我们可以使用闭包来处理按钮点击事件,并访问按钮的属性或其他上下文信息。例如:
  在这个例子中,我们使用闭包来处理按钮的点击事件。闭包捕获了外部变量count,并在每次按钮点击时增加这个变量的值,并在控制台上输出点击次数。

2、异步编程

  闭包在异步编程中也非常有用,因为它可以捕获异步操作发生时的上下文信息。例如,在使用asyncawait关键字进行异步编程时,我们可以使用闭包来访问异步操作的结果或其他上下文信息。例如:

   async Task<int> GetDataAsync(){await Task.Delay(1000);return 10;}int x = 5;Func<int> func = async () =>{int data = await GetDataAsync();return x + data;};int result = await func();Console.WriteLine(result);

  在这个例子中,我们使用闭包来访问异步操作的结果。闭包捕获了外部变量x,并在异步操作完成后将其与异步操作的结果相加,并返回结果。

3、迭代器和 LINQ 查询

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
int sum = 0;
foreach (int number in numbers)
{sum += number;
}
Console.WriteLine(sum);

  闭包在迭代器和 LINQ 查询中也非常有用,因为它可以捕获迭代器或查询的上下文信息。例如,在使用foreach循环或 LINQ 查询时,我们可以使用闭包来访问迭代器或查询的当前元素或其他上下文信息。例如:
  在这个例子中,我们使用foreach循环来遍历一个整数列表,并将每个元素累加到一个变量中。在循环内部,我们使用闭包来捕获外部变量sum,并在每次迭代时将当前元素累加到这个变量中。

五、闭包的注意事项

1、变量捕获的副作用

  闭包捕获外部变量可能会导致一些意想不到的副作用。例如,如果捕获的变量是一个引用类型,并且在闭包内部修改了这个变量的值,那么这个修改可能会影响到其他地方对这个变量的引用。例如:

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };Func<List<int>> func = () => numbers;numbers.Add(6);Console.WriteLine(func().Count);

  在这个例子中,我们定义了一个闭包,它捕获了外部变量numbers。然后,我们在闭包外部修改了这个变量的值,并调用了闭包。由于闭包捕获了外部变量,所以闭包返回的列表也包含了修改后的元素。

2、闭包的性能影响

  闭包可能会对性能产生一些影响,因为它们需要捕获外部变量并在堆上分配内存。在一些性能敏感的场景下,我们可能需要考虑避免使用闭包或者使用其他技术来替代闭包。例如,在一些高性能的计算场景下,我们可以使用结构体而不是类来避免闭包的性能开销。

3、闭包的内存管理

  闭包可能会导致内存泄漏,因为它们可能会捕获外部变量并保持对这些变量的引用。在一些长时间运行的应用程序中,我们需要注意闭包的内存管理,避免不必要的内存泄漏。例如,在使用事件处理时,我们需要注意在不再需要事件处理时取消订阅事件,以避免闭包的内存泄漏。

六、总结

  闭包是 C# 中一个强大的概念,它允许函数捕获外部变量并在函数外部访问这些变量。闭包在很多场景下都非常有用,比如在匿名函数、Lambda 表达式和委托中。在使用闭包时,我们需要注意变量捕获的副作用、性能影响和内存管理等问题,以确保代码的正确性和性能。
在这里插入图片描述

相关文章:

C# 中的闭包

文章目录 前言一、闭包的基本概念二、匿名函数中的闭包1、定义和使用匿名函数2、匿名函数捕获外部变量3、闭包的生命周期 三、Lambda 表达式中的闭包1、定义和使用 Lambda 表达式2、Lambda 表达式捕获外部变量3、闭包的作用域 四、闭包的应用场景1、事件处理2、异步编程3、迭代…...

网络编程 03:端口的定义、分类,端口映射,通过 Java 实现了 IP 和端口的信息获取

一、概述 记录时间 [2024-12-19] 前置文章&#xff1a; 网络编程 01&#xff1a;计算机网络概述&#xff0c;网络的作用&#xff0c;网络通信的要素&#xff0c;以及网络通信协议与分层模型 网络编程 02&#xff1a;IP 地址&#xff0c;IP 地址的作用、分类&#xff0c;通过 …...

制作项目之前的分析

对网页的分析可以从多个角度入手&#xff0c;具体包括内容分析、技术分析、用户体验分析。 以下是对网页分析的详细步骤&#xff0c;帮助你从不同维度评估一个网页的效果与质量&#xff1a; 1. 内容分析 内容是网页最核心的部分&#xff0c;确保其符合用户需求是网页设计的首…...

LeetCode 1925 统计平方和三元组的数目

探索平方和三元组&#xff1a;从问题到 Java 代码实现 在数学与编程的交叉领域&#xff0c;常常会遇到一些有趣且富有挑战性的问题。今天&#xff0c;就让我们深入探讨一下 “平方和三元组” 这个有趣的话题&#xff0c;并使用 Java 语言来实现计算满足特定条件的平方和三元组…...

java开发入门学习三-二进制与其他进制

常见的进制 常用的进制有二进制&#xff0c;八进制&#xff0c;十进制&#xff0c;十六进制。而我们最熟悉的是十进制&#xff0c;他们分别是怎么表达的呢&#xff1f; 定义不同的进制&#xff0c;写法不同 二进制&#xff08;Binary&#xff09;&#xff1a; 使用前缀 0b 或…...

C/S软件授权注册系统(Winform+WebApi+.NET8+EFCore版)

适用软件&#xff1a;C/S系统、Winform桌面应用软件。 运行平台&#xff1a;Windows .NETCore&#xff0c;.NET8 开发工具&#xff1a;Visual Studio 2022&#xff0c;C#语言 数据库&#xff1a;Microsoft SQLServer 2012&#xff0c;Oracle 21c&#xff0c;MySQL8&#xf…...

Linux —— 管理进程

一、查看进程 运行态&#xff08;Running&#xff09; 定义&#xff1a;处于运行态的进程正在 CPU 上执行指令。在单 CPU 系统中&#xff0c;同一时刻只有一个进程处于运行态&#xff1b;在多 CPU 或多核系统中&#xff0c;可能有多个进程同时处于运行态。示例&#xff1a; 当…...

Diffusino Policy学习note

Diffusion Policy—基于扩散模型的机器人动作生成策略 - 知乎 建议看看&#xff0c;感觉普通实验室复现不了这种工作。复现了也没有太大扩展的意义。 Diffusion Policy 是监督学习吗 Diffusion Policy 通常被视为一种基于监督学习的方法&#xff0c;但它的实际训练过程可能结…...

【Python】*args和**kwargs

【Python】*args和**kwargs 一、*args: 接收不定数量的位置参数示例1&#xff1a;简单的加法计算器示例2&#xff1a;转发参数给另一个函数 二、**kwargs: 接收不定数量的关键字参数示例3&#xff1a;创建用户配置文件示例4&#xff1a;合并多个字典 三、组合使用*args和**kwar…...

使用正则表达式提取PDF文件页数的实现方案

文章目录 背景介绍实现原理代码实现1. 基础函数结构2. 页数提取逻辑3. 使用示例 正则表达式解析优点与局限性优点局限性 错误处理建议性能优化建议最佳实践建议总结参考资源 背景介绍 在Web应用开发中,我们经常需要获取上传PDF文件的页数信息。虽然可以使用pdf.js等第三方库,但…...

Android实现RecyclerView边缘渐变效果

Android实现RecyclerView边缘渐变效果 1.前言&#xff1a; 是指在RecyclerView中实现淡入淡出效果的边缘效果。通过这种效果&#xff0c;可以使RecyclerView的边缘在滚动时逐渐淡出或淡入&#xff0c;以提升用户体验。 2.Recyclerview属性&#xff1a; 2.1、requiresFading…...

springboot443旅游管理系统(论文+源码)_kaic

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统旅游管理系统信息管理难度大&#xff0c;容错率低&#…...

利用git上传项目到GitHub

GitHub是基于git实现的代码托管。git是目前最好用的版本控制系统了&#xff0c;非常受欢迎&#xff0c;比之svn更好。 GitHub可以免费使用&#xff0c;并且快速稳定。 利用GitHub&#xff0c;你可以将项目存档&#xff0c;与其他人分享交流&#xff0c;并让其他开发者帮助你一…...

Rust之抽空学习系列(四)—— 编程通用概念(下)

Rust之抽空学习系列&#xff08;四&#xff09;—— 编程通用概念&#xff08;下&#xff09; 1、函数 函数用来对功能逻辑进行封装&#xff0c;能够增强复用、提高代码的可读 以下是函数的主要组成部分&#xff1a; 名称参数返回类型函数体 1.1、函数名称 在Rust中&…...

K-Means 聚类:数据挖掘的瑞士军刀

引言 在数据科学领域&#xff0c;聚类算法是一种非常重要的无监督学习方法&#xff0c;它能够帮助我们发现数据中的自然分组或模式。其中&#xff0c;K-Means 聚类算法因其简单高效而成为最常用的聚类算法之一。无论是市场细分、社交网络分析&#xff0c;还是图像分割等领域&a…...

项目练习:若依-前端项目的目录结构介绍

文章目录 一、目录截图二、目录讲解 一、目录截图 二、目录讲解 1、首先&#xff0c;我们可以看到&#xff0c;这个VUE项目&#xff0c;只有一个App.vue&#xff0c;所以&#xff0c;它是一个单页面系统。 这个App.vue是根组件&#xff0c;root组件。 2、public目录 在Vue 3.…...

知网研学 | 知网文献(CAJ+PDF)批量下载

知网文献&#xff08;CAJPDF&#xff09;批量下载 一、知网研学安装二、插件及脚本安装三、CAJ批量下载四、脚本下载及PDF批量下载浏览器取消拦截窗口 一、知网研学安装 批量下载知网文件&#xff0c;格式为es6文件&#xff0c;需使用知网研学软件打开&#xff0c;故需先安装该…...

设计模式期末复习

一、设计模式的概念以及分类 二、设计模式的主题和意图 三、面向对象程序设计原则&#xff0c;记住名字&#xff0c;还要理解它的使用场景以及如何用&#xff1f; 四、松耦合、紧耦合、强关联、弱关联、静态复用、动态复用的概念&#xff0c;还有静态委派&#xff0c;动态委…...

CentOS7源码编译安装nginx+php+mysql

1.安装nginx 安装依赖 yum -y install gcc gcc-c wget automake autoconf libtool libxml2-devel libxslt-devel perl-devel perl-ExtUtils-Embed pcre-devel openssl openssl-devel 创建一个不能登录的nginx运行用户 groupadd www-data useradd -s /sbin/nologin -g www-d…...

linux CentOS系统上卸载docker

一、停止Docker服务 首先&#xff0c;需要停止Docker服务。使用systemctl命令来停止Docker服务&#xff1a; bash复制代码sudo systemctl stop docker二、卸载Docker软件包 接下来&#xff0c;使用CentOS的包管理器yum来卸载Docker软件包。根据安装的Docker版本和组件&#…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

Kafka主题运维全指南:从基础配置到故障处理

#作者&#xff1a;张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1&#xff1a;主题删除失败。常见错误2&#xff1a;__consumer_offsets占用太多的磁盘。 主题日常管理 …...

WPF八大法则:告别模态窗口卡顿

⚙️ 核心问题&#xff1a;阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程&#xff0c;导致后续逻辑无法执行&#xff1a; var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题&#xff1a…...

【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统

Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...