C#进阶之全面解析Lambda表达式
引言
在实际的项目中遇到一个问题,我们经常在网上搜索复制粘贴,其中有些代码看着非常的简洁,比如Lambda表达式,但是一直没有去深入了解它的由来,以及具体的使用方法,所以在使用的时候比较模糊,其次,编程涉及面比较广,我们不可能每个方面都去精通了解,但经常运到的一些东西,必须了解其具体使用方法及使用场景,才能书写出优美、简洁、可读性强的代码。笔者通过搜索、整理资料及测试代码,详细的介绍Lambda 表达式的用法。
Lambda 表达式概念
“Lambda 表达式”(lambda expression)是一个匿名函数,可以表示为委托的代码,或者表示为表达式树的代码,它所表示的表达式树可以编译为委托。 Lambda 表达式的特定委托类型取决于其参数和返回值。不返回值的 Lambda 表达式对应于 Action
委托,具体取决于其参数数量。 返回值的 Lambda 表达式对应于 Func
委托,具体取决于其参数数量。
Lambda 表达式广泛用于:
将要执行的代码传递给异步方法,例如 Task.Run(Action)。
编写 LINQ 查询表达式。
创建表达式树。
C# 中委托的演变
在 C# 1.0 中,通过使用在代码中其他位置定义的方法显式初始化委托来创建委托的实例。 C# 2.0 引入了匿名方法的概念,作为一种编写可在委托调用中执行的未命名内联语句块的方式。 C# 3.0 引入了 Lambda 表达式,这种表达式与匿名方法的概念类似,但更具表现力并且更简练。 这两个功能统称为匿名函数。 通常,面向 .NET Framework 3.5 及更高版本的应用程序应使用 lambda 表达式。
C#1.0中委托的实现,代码如下:
delegate int CalculateHandler(int x, int y);
private int Sum(int x, int y)
{
return x + y;
}
public void Test()
{
CalculateHandler sumHandler =new CalculateHandler(Sum);
MessageBox.Show(sumHandler(, ).ToString());//输入结果3
}
C#2.0中匿名方法的实现,代码如下:
delegate int CalculateHandler(int x, int y);
public void Test()
{
CalculateHandler sumHandler = delegate(int x, int y) { return x + y; };
MessageBox.Show(sumHandler(, ).ToString());//输入结果3
}
C#3.0中Lambda 表达式的实现,代码如下:
delegate int CalculateHandler(int x, int y);
public void Test()
{
CalculateHandler sumHandler = (x, y) => x + y;
MessageBox.Show(sumHandler(, ).ToString());//输入结果3
}
由此可以看出微软的一步步升级,带给我们的是编程上的优美,简洁,可读性强,因此作为程序员我们要一直处于学习的路上。
Lambda 表达式使用
C#的Lambda 表达式都使用 Lambda 运算符 =>,该运算符读为“goes to”, 若要创建 Lambda 表达式,需要在 lambda 运算符左侧指定输入参数(如果有),然后在另一侧输入表达式或语句块。 例如,单行 Lambda 表达式 x => x * x
指定名为 x
的参数并返回 x
的平方值。
在介绍Lambda 表达式使用之前我们先了解.Net为我们定义好的Action<T>和Func<T>两个泛型委托。
Action<T>泛型委托
Action<T>委托表示引用一个返回类型为Void的方法。这个委托存在不同的变体,可以传递之多16个不同的参数类型。同时,没有泛型参数的Action类可以调用没有参数的方法。例如,Action<in T>表示有一个输入参数的方法,Action<in T1,in T2>表示有两个输入参数的方法。
Func<T>泛型委托
Func<T>可以以类似的方法使用。不过Func<T>允许调用带返回参数的方法。Func<T>也有不同的变体,之多可以传递16个参数和一个返回类型。例如:Func<out TResult>委托类型可以无参的带返回类型的方法,Func<in T1,inT2,out Tresult>表示带两个参数和一个返回类型的方法。
Func<T>可以表示带输出的方法,T可以有多个,且只有最后一个表示输出即最后一个是返回类型。Func<in T1,inT2,out Tresult>中的字符in、out在实际代码中是不会出现的。
表达式 Lambda
表达式位于 =>
运算符右侧的 Lambda 表达式称为“表达式 lambda”。具体形式:(input-parameters) => expression,表达式 lambda 会返回表达式的结果。
具体事例,代码如下:
public Action SuccessPrompt =() => MessageBox.Show("执行成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); //没有参数,括号不能省略
public Func<int, int, bool> Compare = (x, y) => x > y;//判断x是否大于y
语句 Lambda
语句 Lambda 与表达式 lambda 表达式类似,只是语句括在大括号中,具体形式:(input-parameters) => { statement; }。语句 lambda 的主体可以包含任意数量的语句;但是,实际上通常不会多于两个或三个。
具体事例代码如下:
public Action<string> Prompt = prompt =>
{
MessageBox.Show(prompt, "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
};//仅当 Lambda 只有一个输入参数时,括号才是可选的;否则括号是必需的
异步 Lambda
通过使用 async 和 await 关键字,你可以轻松创建包含异步处理的 lambda 表达式和语句。其中async
和 await
关键字是在 C# 5 中引入的。
await关键字
await
运算符应用于异步方法中的任务,在方法的执行中插入挂起点,直到所等待的任务完成。 任务表示正在进行的工作。
await
仅可用于由 async 关键字修改的异步方法中。 使用 async
修饰符定义并且通常包含一个或多个 await
表达式的这类方法称为异步方法。
async修饰符
使用 async
修饰符可将方法、lambda 表达式或匿名方法指定为异步。 如果对方法或表达式使用此修饰符,则其称为异步方法。
使用异步 lambda 添加事件处理程序。 若要添加此处理程序,请在 lambda 参数列表前添加 async
修饰符,代码如下:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
button1.Click += async (sender, e) =>
{
await ExampleMethodAsync();
textBox1.Text += "\r\nControl returned to Click event handler.\n";
};
} private async Task ExampleMethodAsync()
{
// The following line simulates a task-returning asynchronous process.
await Task.Delay();//Task.Delay方法只会延缓异步方法中后续部分执行时间,当程序执行到await表达时,一方面会立即返回调用方法,执行调用方法中的剩余部分,这一部分程序的执行不会延长。另一方面根据Delay()方法中的参数,延时对异步方法中后续部分的执行。
}
}
Lambda 表达式和元组
自 C# 7.0(对应 .NET Framework4.7和Visual Studio 2017 )起,C# 语言提供对元组的内置支持。 可以提供一个元组作为 Lambda 表达式的参数,同时 Lambda 表达式也可以返回元组。 在某些情况下,C# 编译器使用类型推理来确定元组组件的类型。可通过用括号括住用逗号分隔的组件列表来定义元组,通常,元组字段命名为 Item1
、Item2
等等。但是,可以使用命名组件定义元组。
事例代码如下:
public void Test1()
{
Func<(int, int, int), (int, int, int)> doubleItem = ns => ( * ns.Item1, * ns.Item2, * ns.Item3);
var itemList = (, , );
var resultDItemList = doubleItem(itemList);//结果为[2, 4, 6]
} public void Test2()
{
Func<(int x, int y, int z), (int, int, int)> doubleItem = ns => ( * ns.x, * ns.y, * ns.z);
var itemList = (, , );
var resultDItemList = doubleItem(itemList);//结果为[2, 4, 6]
}
含标准查询运算符的 Lambda
在其他实现中,LINQ to Objects 有一个输入参数,其类型是泛型委托 Func<TResult> 系列中的一种。 这些委托使用类型参数来定义输入参数的数量和类型,以及委托的返回类型。 Func
委托对于封装用户定义的表达式非常有用,这些表达式将应用于一组源数据中的每个元素。
标准查询运算符是组成 LINQ 模式的方法。 这些方法中的大多数都作用于序列;其中序列指其类型实现 IEnumerable<T> 接口或 IQueryable<T> 接口的对象。 标准查询运算符提供包括筛选、投影、聚合、排序等在内的查询功能。
共有两组 LINQ 标准查询运算符,一组作用于类型 IEnumerable<T> 的对象,另一组作用于类型 IQueryable<T> 的对象。 构成每个集合的方法分别是 Enumerable 和 Queryable 类的静态成员。 这些方法被定义为作为方法运行目标的类型的扩展方法。 这意味着可以使用静态方法语法或实例方法语法来调用它们。
具体事例代码如下:
public class Sutdent
{
public string Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
static void Main(string[] args)
{
List<Sutdent> studentList = new List<Sutdent>();
studentList.Add(new Sutdent {Id = "", Name = "张三", Age = });
studentList.Add(new Sutdent {Id = "", Name = "李四", Age = });
studentList.Add(new Sutdent {Id = "", Name = "王五", Age = });
studentList.Add(new Sutdent {Id = "", Name = "赵六", Age = }); List<Sutdent> list1 = studentList.FindAll(st => st.Age > );//选择年龄大于17的所有学生
List<Sutdent> list2 = studentList.Where(st => st.Age > ).ToList();//选择年龄大于17的所有学生
studentList.Sort((st1,st2)=>st2.Age-st1.Age);//按Age降序排列
List<string> list3 = studentList.Select(st => st.Name).ToList();//选择列表中的所有名字 }
Lambda 表达式中的类型推理
编写 Lambda 时,通常不必为输入参数指定类型,因为编译器可以根据 Lambda 主体、参数类型以及 C# 语言规范中描述的其他因素来推断类型。
lambda 类型推理的一般规则如下:
Lambda 包含的参数数量必须与委托类型包含的参数数量相同。
Lambda 中的每个输入参数必须都能够隐式转换为其对应的委托参数。
Lambda 的返回值(如果有)必须能够隐式转换为委托的返回类型。
总结
通过上边的讲解,我们可以看出Lambda表达式的用法非常的简单,特别在标准查询运算符中应用非常广泛,提高了编程效率,且写出的代码非常的简洁。文中若有不足之处,还望海涵,博文写作不易希望多多支持,后续会更新更多内容,感兴趣的朋友可以加关注,欢迎留言交流!
C#进阶之全面解析Lambda表达式的更多相关文章
- 解析 Lambda 表达式
我们先创建一个表达式树: Expression<Func<int, int, int>> expression = (a,b) => a + b; 我们的例子是一个Exp ...
- .NET进阶篇05-Linq、Lambda表达式
知识需要不断积累.总结和沉淀,思考和写作是成长的催化剂 内容目录 一.Lambda表达式1.匿名方法2.Lambda表达式二.Linq概述三.查询操作符1.linq初见2.常用查询操作符筛选排序分组连 ...
- python进阶(1)Lambda表达式
Lambda表达式 lambda表示的是匿名函数,不需要用def来声明,一句话就可以声明出一个函数 语法 函数名 = lambda 参数:返回值 注意点 1.函数的参数可以有多个,多个参数之间用逗号隔 ...
- Lambda表达式树解析(下)
概述 前面章节,总结了Lambda树的构建,那么怎么解析Lambda表达式树那?Lambda表达式是一种委托构造而成,如果能够清晰的解析Lambda表达式树,那么就能够理解Lambda表达式要传递的正 ...
- Lambda表达式树解析(下)包含自定义的provider和查询
概述 前面章节,总结了Lambda树的构建,那么怎么解析Lambda表达式树那?Lambda表达式是一种委托构造而成,如果能够清晰的解析Lambda表达式树,那么就能够理解Lambda表达式要传递的正 ...
- Lambda表达式的前世今生
Lambda 表达式 早在 C# 1.0 时,C#中就引入了委托(delegate)类型的概念.通过使用这个类型,我们可以将函数作为参数进行传递.在某种意义上,委托可理解为一种托管的强类型的函数指针. ...
- 说说lambda表达式与表达式树(未完)
Lambda表达式可以转换成为代码(委托)或者数据(表达式树).若将其赋值给委托,则Lambda表达式将转换为IL代码:如果赋值给 Expression<TDelegate>,则构造出一颗 ...
- lambda表达式封装对数据库的查询
前言: 1.为什么要封装lambda表达式数据库查询,原因有一下几点: 1.1.在以往的开发中进行数据库表查询时,其实所需要的字段就是其中几个,但是在开发中,开发者往往习惯select * 进行查询, ...
- Lambda表达式树构建(上)
概述 Lambda是C#常用的语句,采用委托等方式,来封装真实的代码块.Lambda其实就是语法糖,是一个匿名函数,是一种高效的类似于函数式编程的表达式,Lambda简化了开发中需要编写的代码量.它可 ...
随机推荐
- 2017年江西理工大学C语言程序设计竞赛(高级组)
问题 A: 求近似值 #include <stdio.h> #include <time.h> #include <stdlib.h> using namespac ...
- DbUtils(一) 结果集概览
记录自己对DbUtils的学习和了解 我感觉Dbutils用的最多的就是对查询结果集的处理,就以这个开始了解Dbutils库. 查看源代码发现结果集的转换主要用于query,insert, ...
- python入门之sys模块、shutil模块
sys模块 import sys sys.version 返回python的版本 sys.argv 返回一个以脚本名,和传入的参数作为元素的列表 sys.path 返回一个以当前代码文件路径,pyth ...
- HDU - 5920 Ugly Problem 求解第一个小于n的回文数
http://acm.hdu.edu.cn/showproblem.php?pid=5920 http://www.cnblogs.com/xudong-bupt/p/4015226.html 把前半 ...
- (转)老男孩linux培训某节课前考试试题及答案分享
目录:[考试目的] ................................2[考试范围] ...............................2[答题策略] .......... ...
- (转)Linux硬链接、软链接及inode详解
inode 文件储存在硬盘上,硬盘的最小存储单位叫做“扇区”(Sector).每个扇区储存512字节(相当于0.5KB). 操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读 ...
- Hadoop 解除 “Name node is in safe mode”(转)
运行Hadoop程序时,有时候会报以下错误: org.apache.hadoop.dfs.SafeModeException: Cannot delete /user/hadoop/input. Na ...
- Entity Framework小知识
记录在使用EF中使用的技巧,以备查阅. 1.当需要查询一个列总和的时候,如果列是允许NULL或者未查到信息的时候,想要返回的是0 而非NULL时 db.表名.Sum(p=> (decimal?) ...
- MvcPager.dll使用实现无刷新分页以及MvcPager的Nuget程序包实现刷新分页
无刷新分页: 1.引入JQuery的NuGet程序包 2.引入程序包 3.引入MvcPager.dll ,MvcPager.dll文件下载链接http://pan.baidu.com/s/1hsvB ...
- 记秋招第一个offer:去哪儿
9月17日 网申去哪儿,没有内推,因为网申了就不能内推了.难受,内推可以免简历筛选的,这下好了,可能简历直接挂了.我怎么犯了这么低级的错误?还没去搞清楚能不能内推就先傻乎乎地网申了. 9月28日 晚上 ...