C# Lambda 表达式 详解
总目录
前言
在C#编程中,Lambda表达式是一种简洁而强大的语法特性,它提供了一种更加灵活和直观的方式来编写匿名函数。无论是在LINQ查询、事件处理还是异步编程中,Lambda表达式都扮演着重要角色。本文将详细介绍Lambda,帮助您更好地理解和掌握这一重要的C#特性。
一、Lambda 表达式 是什么?
1. 定义
Lambda表达式是C# 3.0引入的一个新特性,它是匿名方法的简化形式,允许开发者以更简洁的方式定义函数。Lambda表达式可以用于任何需要委托的地方,并且可以在运行时动态创建函数对象。它的主要优点包括代码简洁、易于阅读和维护。
2. 基本语法
Lambda表达式的语法非常简单,由参数列表、=>
符号和表达式或语句块组成。其基本形式如下:
(parameters) => expression
或
(parameters) => { statements }
- 参数列表:可以包含零个或多个参数,用逗号分隔。
=>
符号:称为“goes to
”操作符,表示从参数到表达式或语句块的映射。- 表达式或语句块:可以是一个简单的表达式或一个包含多条语句的代码块。
二、如何使用 Lambda 表达式?
根据是否包含语句块,Lambda表达式可以分为两种类型:表达式Lambda 和 语句Lambda。
1. 使用 表达式Lambda
表达式Lambda是最简单的形式,仅包含一个表达式,表达式Lambda 侧重于表达式的返回值。其语法如下:
(parameters) => expression//如:x => x * 4
1)演化过程
public int Square(int num)
{return num * num;
}
上面定义一个简单的计算方法,现在我们需要以 Lambda 表达式表现出来,该如何表现呢?
Func<int, int> func = x => x * x;
上述两个示例代码是等效的, x => x * x;
本质上等同于将一个有返回值的方法赋予了Func
委托;
2)示例
// 定义一个委托类型
Func<int, int> square = x => x * x;// 调用委托
Console.WriteLine(square(5)); // 输出: 25
在这个例子中,我们定义了一个名为 square
的委托,它接受一个整数参数并返回该整数的平方值。通过Lambda表达式,我们可以非常简洁地实现这一功能。
2. 语句Lambda
语句Lambda允许包含多条语句,适用于需要执行复杂逻辑的情况。其语法如下:
(parameters) => { statements }
语句Lambda侧重语句块中执行内容,具体示例如下:
// 定义一个委托类型
Action<int> printAndSquare = x =>
{Console.WriteLine($"Input: {x}");Console.WriteLine($"Square: {x * x}");
};// 调用委托
printAndSquare(5); // 输出: Input: 5 Square: 25
在这个例子中,我们定义了一个名为 printAndSquare
的委托,它接受一个整数参数并打印输入值及其平方值。通过语句Lambda,我们可以轻松实现这一复杂的逻辑。
Func<int, int> factorial = n =>
{if (n == 0)return 1;elsereturn n * factorial(n - 1);
};
3. Lambda 表达式的各种使用方式
1)无参数 与 空括号
将 lambda 表达式的输入参数括在括号中。 使用空括号指定零个输入参数。如果是没有参数,必须有这个空括号
Action line = () => Console.WriteLine();
2)方法体只有一行代码
如果方法体中只有一行代码,可以省略方法体大括号
Action line1 = () => { Console.WriteLine("test"); };//省略方法体大括号
Action line2 = () => Console.WriteLine("test");
3)类型推断
C# 编译器会根据上下文自动推断参数类型,无需显式声明:
Lambda表达式的类型是由编译器根据上下文推断出来的,通常对应于某个委托类型(如
Func<T, TResult>
或Action<T>
)。如果Lambda表达式没有返回值,则其类型为Action
;如果有返回值,则其类型为Func
。
Func<int, int, int> func3 = (int num1, int num2) => num1 * num2;//类型推断 允许我们省略 数据类型的显示声明
Func<int, int, int> func4 = (num1, num2) => num1 * num2;
4)无法类型推断时
一般情况下,编译器可自行推断输入参数的类型,但存在编译器无法推断的情况,则需自行指定
Func<int, string, bool> isTooLong = (int x, string s) => s.Length > x;
5)省略return
如果方法体中只有一行代码,且有返回值,可以省略return
- 当方法体中只有一行代码,但并没有返回值的时候,此时的Lambda表达式 是 【语句
Lambda
】 - 当方法体中只有一行代码,且有返回值的时候,此时的Lambda表达式 是 【表达式
Lambda
】
// 语句 Lambda ,侧重于执行内容
Action action = () => Console.WriteLine("ss");// 表达式 Lambda,侧重于返回值
// 特征:一行代码 + 有返回值
Func<int, int> func1 = x => x * x * x;
Func<int, string> func2 = x => x.ToString();
6)一个参数
如果只有一个输入参数,则括号可有可无
Func<int, int> func = (x) => x * x * x;//省略括号
Func<double, double> cube = x => x * x * x;
7) 2个参数以上
两个或更多输入参数使用逗号加以分隔
Func<int, int, bool> testForEquality = (x, y) => x == y;Func<int, int, int> add = (x, y) => x + y;
8)多行代码 + 显示 return
当方法体中具有多行代码,则 该Lambda表达式 是【语句Lambda】,如果需要返回数据,则需要显示使用return
。
Func<int, int, int> func3 = (num1, num2) =>
{Console.WriteLine("计算:");return num1* num2;
};
三、Lambda 表达式的常见应用场景
1. 应用场景
Lambda表达式广泛应用于各种场景,以下是几个常见的应用场景:
- LINQ查询:在LINQ查询中,Lambda表达式常用于指定过滤条件、投影、排序等操作。
- 事件处理:可以用Lambda表达式来简化事件处理程序的编写。
- 异步编程:在异步编程中,Lambda表达式可用于定义异步任务的执行逻辑。
- 委托与回调:Lambda表达式可以作为委托的实现,用于回调函数或其他需要传递函数指针的场合。
2. 应用场景示例
1)Lambda与委托
- 内置委托类型 :Lambda表达式可隐式转换为
Func
或Action
委托:// Func委托(带返回值) Func<int, int, int> add = (a, b) => a + b; // Action委托(无返回值) Action<string> log = msg => Console.WriteLine(msg);
Lambda 表达式可以转换的委托类型由其参数和返回值的类型定义。 如果 Lambda 表达式不返回值,则可以将其转换为 Action 委托类型之一;否则,可将其转换为 Func 委托类型之一。
- 自定义委托 :Lambda可直接赋值给自定义委托类型:
delegate int Operation(int a, int b); Operation add = (x, y) => x + y; Console.WriteLine(add(3, 4)); // 输出 7
2)Lambda 在LINQ中的应用
Lambda是LINQ查询的基石,是编写查询条件的标准方式,用于定义筛选、排序和转换逻辑:
var numbers = new List<int> { 1, 2, 3, 4, 5 };
// 筛选大于3的元素
var filtered = numbers.Where(n => n > 3); // 筛选 >3 的数
// 转换为平方值
var squares = numbers.Select(n => n * n); // 转换为平方值
public class Program
{public static void Main(){var numbers = new List<int> { 1, 2, 3, 4, 5 };// 筛选var evenNumbers = numbers.Where(x => x % 2 == 0);Console.WriteLine("Even numbers: " + string.Join(", ", evenNumbers));// 排序var sortedNumbers = numbers.OrderBy(x => x);Console.WriteLine("Sorted numbers: " + string.Join(", ", sortedNumbers));// 投影var squaredNumbers = numbers.Select(x => x * x);Console.WriteLine("Squared numbers: " + string.Join(", ", squaredNumbers));}
}
- 表达式树:Lambda还可生成表达式树,供LINQ to SQL等框架解析为SQL语句。
3) Lambda 在事件处理中的应用
Lambda 可简化事件处理程序的编写:
button.Click += (sender, e) => Console.WriteLine("按钮被点击!");
4)Lambda 在异步编程中的应用
在异步编程中,Lambda表达式可以用于定义异步任务的执行逻辑。
class Program
{static async Task Main(){await Task.Run(() =>{Console.WriteLine("Task started.");for (int i = 0; i < 5; i++){Console.WriteLine($"Processing item {i + 1}");}Console.WriteLine("Task completed.");});Console.WriteLine("Main method completed.");}
}
在这个例子中,我们使用Lambda表达式 () => { ... }
来定义异步任务的执行逻辑。
三、高级特性
1. 闭包(Closure)
Lambda 表达式可以捕获上下文中(或 外部作用域 )的变量,形成闭包。
Lambda表达式可以捕获其定义范围内的局部变量,这意味着Lambda表达式可以访问外部作用域中的变量。这种特性使得Lambda表达式在某些情况下非常强大和灵活。
public class Program
{public static void Main(){int factor = 2;Func<int, int> multiply = x => x * factor;Console.WriteLine(multiply(5)); // 输出: 10factor = 5;Console.WriteLine(multiply(5)); // 输出: 25(闭包保留了 factor 的引用)}
}
在这个例子中,Lambda表达式捕获了外部变量 factor
,并在计算结果时使用了这个变量的值。
需要注意的是,被捕获的变量在Lambda表达式被调用时仍然有效,因此要确保这些变量在其生命周期内不会被销毁或修改。
注意事项:
- 捕获的变量生命周期与委托绑定,可能导致内存泄漏;
- 避免修改外部变量,防止逻辑混乱。
- 闭包可能导致意外的变量共享,需谨慎处理。
2. 表达式树(Expression Tree)
Lambda 表达式可以转换为表达式树,用于表示代码的结构化形式。表达式树常用于动态生成代码或进行反射操作。
public class Program
{public static void Main(){Expression<Func<int, int>> expression = x => x + 1;Console.WriteLine(expression.ToString()); // 输出: x => (x + 1)}
}
3. 异步 Lambda
Lambda 表达式可以用于异步操作,通过 async
和 await
关键字实现。
Func<Task<int>> asyncLambda = async () => await Task.Delay(1000).ContinueWith(_ => 42);
using System;
using System.Threading.Tasks;public class Program
{public static void Main(){Func<int, Task<int>> asyncLambda = async x =>{await Task.Delay(1000);return x * x;};asyncLambda(5).ContinueWith(t => Console.WriteLine(t.Result)); // 输出: 25}
}
Func<Task> asyncTask = async () =>
{ await Task.Delay(1000); Console.WriteLine("异步完成");
};
4. 动态方法
Lambda 表达式可以用于动态生成方法,这在反射或动态调用中非常有用。
using System;
using System.Reflection.Emit;public class Program
{public static void Main(){// 动态生成一个方法DynamicMethod dynamicMethod = new DynamicMethod("Add", typeof(int), new[] { typeof(int), typeof(int) }, typeof(Program).Module);ILGenerator il = dynamicMethod.GetILGenerator();il.Emit(OpCodes.Ldarg_0);il.Emit(OpCodes.Ldarg_1);il.Emit(OpCodes.Add);il.Emit(OpCodes.Ret);// 调用动态生成的方法Func<int, int, int> add = (Func<int, int, int>)dynamicMethod.CreateDelegate(typeof(Func<int, int, int>));Console.WriteLine(add(2, 3)); // 输出: 5}
}
5. 泛型 Lambda
Lambda 表达式可以用于泛型委托,提高代码的复用性增强灵活性。
Func<T, string> toString = (T obj) => obj?.ToString();
四、注意事项与限制
-
作用域规则:
- Lambda 无法捕获
ref
/out
参数。 - Lambda 内的
return
不影响外部方法。 - 外部方法无法访问 Lambda 内部定义的变量。
- Lambda 无法捕获
-
运算符限制:
- 不可用于
is
或as
运算符左侧。
- 不可用于
-
语句限制:
- 不允许包含
goto
、break
、continue
跨越 Lambda 边界的语句。
- 不允许包含
-
性能考量:
- 表达式树可能比直接委托生成更多内存开销。
- 频繁调用的Lambda需避免闭包捕获,减少GC压力。
- 过度使用Lambda表达式可能影响性能,尤其是在循环中频繁创建委托时。
- Lambda 表达式在运行时会被编译为委托或表达式树,可能会有一定的性能开销。在性能敏感的场景中,需要谨慎使用。
-
循环变量捕获:
- Lambda表达式捕获循环变量时,可能会导致意外行为(多线程异步情况下)。应使用局部变量来避免。
for (int i = 0; i < 3; i++) {int local = i;actions.Add(() => Console.WriteLine(local)); }
五、演化过程
public delegate void ShowDelegate(int a,string b);public class LambdaEvolution{public void Show(int a1,string b1){Console.WriteLine($"show{a1}:{b1}");}public void Test(){//1 .Netframework1.0/1.1,原始方法ShowDelegate showDelegate = new ShowDelegate(Show);//2 .NetFramework2.0,匿名方法,增加delegate,去掉单独定义方法showDelegate = delegate (int x,string y) { Console.WriteLine($"show{x}:{y}"); };//3 .NetFramework3.0,=> 引入Lambda表达式,去掉delegateshowDelegate = (int s,string t) => { Console.WriteLine($"show{s}:{t}"); };//4 .NetFramework3.0后期,简化参数类型,编译器自动推断数据类型showDelegate = (s,t) => { Console.WriteLine($"show{s}:{t}"); };//5 如果方法体中只有一行代码,可以省略方法体大括号showDelegate = (s,t) => Console.WriteLine($"show{s}:{t}");//如果方法只有一个参数,可以省略参数小括号Action<string> action = x => Console.WriteLine($"show{x}");//如果方法体中只有一行代码,且有返回值,可以省略returnFunc<int, string> func = x => x.ToString();}}
结语
回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。
相关文章:
C# Lambda 表达式 详解
总目录 前言 在C#编程中,Lambda表达式是一种简洁而强大的语法特性,它提供了一种更加灵活和直观的方式来编写匿名函数。无论是在LINQ查询、事件处理还是异步编程中,Lambda表达式都扮演着重要角色。本文将详细介绍Lambda,帮助您更好…...
wordpress自定the_category的输出结构
通过WordPress的过滤器the_category来自定义输出内容。方法很简单,但是很实用。以下是一个示例代码: function custom_the_category($thelist, $separator , $parents ) {// 获取当前文章的所有分类$categories get_the_category();if (empty($categ…...

HTML前端手册
HTML前端手册 记录前端框架在使用过程中遇到的各种问题和解决方案,供后续快速进行手册翻阅使用 文章目录 HTML前端手册1-前端框架1-TypeScript框架2-CSS框架 2-前端Demo1-Html常用代码 2-知云接力3-Live2D平面动画 3-前端运维1-NPM版本管理 1-前端框架 1-TypeScrip…...

vscode mac版本 配置git
首先使用 type -a git查看git的安装目录 然后在vscode中找到settings配置文件,修改git.path...

爬虫Incapsula reese84加密案例:Etihad航空
声明: 该文章为学习使用,严禁用于商业用途和非法用途,违者后果自负,由此产生的一切后果均与作者无关 一、找出需要加密的参数 1.js运行 atob(‘aHR0cHM6Ly93d3cuZXRpaGFkLmNvbS96aC1jbi8=’) 拿到网址,F12打开调试工具,随便搜索航班,切换到network搜索一个时间点可以找…...

【C#】async与await介绍
1. 实例1 1.1 代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace ConsoleApp1 {class Program{static void Main(string[] args){Method1();Method2();Console.ReadKey();}public static…...

【银河麒麟高级服务器操作系统实例】虚拟机桥接网络问题分析及处理
更多银河麒麟操作系统产品及技术讨论,欢迎加入银河麒麟操作系统官方论坛 https://forum.kylinos.cn 了解更多银河麒麟操作系统全新产品,请点击访问 麒麟软件产品专区:https://product.kylinos.cn 开发者专区:https://developer…...

Vue3路由组件和一般组件 切换路由时组件挂载和卸载 路由的工作模式
路由组件和一般组件 路由组件 一般放到pages或view目录 一般组件 一般放到component目录 切换路由 切换路由时,组件和执行挂载和卸载 路由的工作模式 Hash模式 缺点 1.不美观,路径带#号 优点 1.兼容性好 一般适用于管理系统 History模式 缺点…...

Spring Boot集成Minio笔记
一、首先配置MinIO 1、MinIO新建Bucket,访问控制台如图 创建访问密钥(就是账号和密码) 二、集成mino添加Minio客户端依赖 1.maven构建方式在pom.xml引入jar <dependency><groupId>io.minio</groupId><artifactId>minio</artifactI…...
linux c++11 gcc4 环境编译安装googletest/gtest v1.10
c11对应googletest/gtest 经过测试,c11对应版本是googletest v1.10.x 编译安装 编译环境 sudo apt-get update sudo apt-get install -y build-essential cmake下载或git clone代码 git clone https://github.com/google/googletest.git cd googletest git che…...

20250306-笔记-精读class CVRPEnv:step(self, selected)
文章目录 前言一、时间步小于 41.1 控制时间步的递增1.2 判断是否在配送中心1.3 特定时间步的操作1.4更新1.4.1 更新当前节点和已选择节点列表1.4.2 更新需求和负载1.4.3 更新访问标记1.4.4 更新负无穷掩码1.4.5 更新步骤状态,将更新后的状态同步到 self.step_state…...
文档进行embedding,Faiss向量检索
这里采用Langchain的HuggingFaceEmbeddings 参照博主,改了一些东西,因为Langchain0.3在0.2的基础上进行了一定的修改 from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_huggingface import HuggingFaceEmbeddings …...

一周学会Flask3 Python Web开发-在模板中渲染WTForms表单视图函数里获取表单数据
锋哥原创的Flask3 Python Web开发 Flask3视频教程: 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 为了能够在模板中渲染表单,我们需要把表单类实例传入模板。首先在视图函数里实例化表单类LoginForm,然…...

Android AudioFlinger(五)—— 揭开AudioMixer面纱
前言: 在 Android 音频系统中,AudioMixer 是音频框架中一个关键的组件,用于处理多路音频流的混音操作。它主要存在于音频回放路径中,是 AudioFlinger 服务的一部分。 上一节我们讲threadloop的时候,提到了一个函数pr…...
分类学习(加入半监督学习)
#随机种子固定,随机结果也固定 def seed_everything(seed):torch.manual_seed(seed)torch.cuda.manual_seed(seed)torch.cuda.manual_seed_all(seed)torch.backends.cudnn.benchmark Falsetorch.backends.cudnn.deterministic Truerandom.seed(seed)np.random.see…...
Serilog: 强大的 .NET 日志库
Serilog 是一个功能强大的日志记录库,专为 .NET 平台设计。它提供了丰富的 API 和可插拔的输出器及格式化器,使得开发者能够轻松定制和扩展日志记录功能。在本文中,我们将探索 Serilog 的基础知识、API 使用、配置和一些常见的示例。 1. 日志…...

Matlab——添加坐标轴虚线网格的方法
第一步:在显示绘制图的窗口,点击左上角 “编辑”,然后选“坐标区属性” 第二步:点 “网格”,可以看到添加网格的方框了...
π0及π0_fast的源码解析——一个模型控制7种机械臂:对开源VLA sota之π0源码的全面分析,含我司微调π0的部分实践
前言 ChatGPT出来后的两年多,也是我疯狂写博的两年多(年初deepseek更引爆了下),比如从创业起步时的15年到后来22年之间 每年2-6篇的,干到了23年30篇、24年65篇、25年前两月18篇,成了我在大模型和具身的原始技术积累 如今一转眼已…...

TCP7680端口是什么服务
WAF上看到有好多tcp7680端口的访问信息 于是上网搜索了一下,确认TCP7680端口是Windows系统更新“传递优化”功能的服务端口,个人理解应该是Windows利用这个TCP7680端口,直接从内网已经具备更新包的主机上共享下载该升级包,无需从微…...
服务器python项目部署
角色:root, 其他用户应该也可以 1. 安装python3环境 #如果是新机器,尽量执行,避免未知报错 yum -y update python -v yum install python3 python3 -v2. 使用virtualenvwrapper 创建虚拟环境,并使用workon切换不同的虚拟环境 # 安装virtua…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...

【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...

免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...