引言

在实际的项目中遇到一个问题,我们经常在网上搜索复制粘贴,其中有些代码看着非常的简洁,比如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# 编译器使用类型推理来确定元组组件的类型。可通过用括号括住用逗号分隔的组件列表来定义元组,通常,元组字段命名为 Item1Item2 等等。但是,可以使用命名组件定义元组。

事例代码如下:

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表达式的更多相关文章

  1. 解析 Lambda 表达式

    我们先创建一个表达式树: Expression<Func<int, int, int>> expression = (a,b) => a + b; 我们的例子是一个Exp ...

  2. .NET进阶篇05-Linq、Lambda表达式

    知识需要不断积累.总结和沉淀,思考和写作是成长的催化剂 内容目录 一.Lambda表达式1.匿名方法2.Lambda表达式二.Linq概述三.查询操作符1.linq初见2.常用查询操作符筛选排序分组连 ...

  3. python进阶(1)Lambda表达式

    Lambda表达式 lambda表示的是匿名函数,不需要用def来声明,一句话就可以声明出一个函数 语法 函数名 = lambda 参数:返回值 注意点 1.函数的参数可以有多个,多个参数之间用逗号隔 ...

  4. Lambda表达式树解析(下)

    概述 前面章节,总结了Lambda树的构建,那么怎么解析Lambda表达式树那?Lambda表达式是一种委托构造而成,如果能够清晰的解析Lambda表达式树,那么就能够理解Lambda表达式要传递的正 ...

  5. Lambda表达式树解析(下)包含自定义的provider和查询

    概述 前面章节,总结了Lambda树的构建,那么怎么解析Lambda表达式树那?Lambda表达式是一种委托构造而成,如果能够清晰的解析Lambda表达式树,那么就能够理解Lambda表达式要传递的正 ...

  6. Lambda表达式的前世今生

    Lambda 表达式 早在 C# 1.0 时,C#中就引入了委托(delegate)类型的概念.通过使用这个类型,我们可以将函数作为参数进行传递.在某种意义上,委托可理解为一种托管的强类型的函数指针. ...

  7. 说说lambda表达式与表达式树(未完)

    Lambda表达式可以转换成为代码(委托)或者数据(表达式树).若将其赋值给委托,则Lambda表达式将转换为IL代码:如果赋值给 Expression<TDelegate>,则构造出一颗 ...

  8. lambda表达式封装对数据库的查询

    前言: 1.为什么要封装lambda表达式数据库查询,原因有一下几点: 1.1.在以往的开发中进行数据库表查询时,其实所需要的字段就是其中几个,但是在开发中,开发者往往习惯select * 进行查询, ...

  9. Lambda表达式树构建(上)

    概述 Lambda是C#常用的语句,采用委托等方式,来封装真实的代码块.Lambda其实就是语法糖,是一个匿名函数,是一种高效的类似于函数式编程的表达式,Lambda简化了开发中需要编写的代码量.它可 ...

随机推荐

  1. python大战机器学习——半监督学习

    半监督学习:综合利用有类标的数据和没有类标的数据,来生成合适的分类函数.它是一类可以自动地利用未标记的数据来提升学习性能的算法 1.生成式半监督学习 优点:方法简单,容易实现.通常在有标记数据极少时, ...

  2. 文件拷贝io nio比较

    import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedRead ...

  3. python3.6下安装wingIDE破解方法

    1.wingIDE的下载: 在电脑配置好的python环境情况下,去官网下载wingIDE6,按照一般方式安装好.安装好它会自动提示你是否激活,你点击激活.然后到下一步. 2.脚本的制作: impor ...

  4. Hadoop InputFormat详解

    InputFormat是MapReduce编程模型包括5个可编程组件之一,其余4个是Mapper.Partitioner.Reducer和OutputFormat. 新版Hadoop InputFor ...

  5. Python Class __init__ __del__ 构造,析构过程解析【转】

    转载自: http://blog.csdn.net/bbdxf/article/details/25774763 最近学习<Python参考手册>即<Learning Python& ...

  6. java NIO-java.io

    1. 传统IO-面向流 1.1 基于字节的IO接口 In/OutputStream 1.2 基于字符的IO接口 Reader/Writer Reader提供抽象方法: int read(char cb ...

  7. 《从0到1学习Flink》—— Data Sink 介绍

    前言 再上一篇文章中 <从0到1学习Flink>-- Data Source 介绍 讲解了 Flink Data Source ,那么这里就来讲讲 Flink Data Sink 吧. 首 ...

  8. 判断两个IP地址是不是属于同一子网的方法

    一个IP地址有三种写法: 第一种,单个IP,如192.168.55.28 第二种,IP/子网掩码,如192.168.55.28/255.255.255.0 第三种,IP/子网掩码长度,如192.168 ...

  9. Java继承改进

    一.java继承改进 首先,多继承的缺点: 1.继承多个父类,父类中方法名相同,产生歧义 2.父类中方法同名,子类未覆盖,也会歧义 所以,java改进,类只能单继承,接口可以多继承 接口中只有抽象方法 ...

  10. jQuery 获取和设置表单元素

    jQuery提供了val()方法,使用它我们可以快速地获取和设置表单的文本框.单选按钮.以及单选按钮的值. 使用val()不带参数,表示获取元素的值 使用val()给定参数,则表示把值赋给元素 如下: ...