C#进阶——Lambad表达式总结
一、概述
Lambda表达式是C#3.0引入的结构,主要是为了简化C#编程。学习其确实有点难理解,但是学会了用起来是真的舒服,主要是特别灵活。
Lambda表达式有很多用处,最常用的是和匿名方法结合,还有LINQ、属性、异步、委托等待!
个人觉得其分为两种用途,一种表示匿名方法传给委托,另一种则为Expression-bodied
以下内容是结合对官方文档理解所写,欢迎拍砖头!
二、格式
·格式一
(input-parameters) => { statements } / expression
该格式是一种委托,可以理解为匿名函数的扩展。
1. “input-parameters” —— 在小括号()里面,它是没有类型化的参数,为什么不用声明参数类型,其实编译器会根据上下文(根据委托的签名)推断出其类型,有点像动态语言的做法。如果有且只有一个参数小括号可以省略。
2. “=>” —— 运算符。该运算符读作 "goes to",把参数与表达式分开。
3. "statements" / "expression" —— 语句或者表达式,如果是语句,需要放到{}里面,如果是表达式,可以省略{}。
4.当有返回值时,如果是语句,不需要return,编译器会自动计算返回值;如果是表达式,则需要return。
5.任何 Lambda 表达式都可以转换为委托类型。 Lambda 表达式可以转换的委托类型由其参数和返回值的类型定义。 如果 lambda 表达式不返回值,则可以将其转换为 Action
委托类型之一;否则,可将其转换为 Func
委托类型之一。(官网摘取)
·格式二
member => expression;
该格式表示是Expression-bodied 方法,将lambda 表达式的这种用法扩展到了方法、属性等成员上,表示成员的实现。
1.“member“ —— 成员,是指方法、属性、构造函数等待
2. “=>” —— 运算符。
3.”expression“ —— 表达式,注意这里必须是一条表达式,不能是语句块,且不能有花括号”{}“。
三、Lambda表达式委托
·匿名方法
如果方法只被调用一次,且方法体比较少,我们往往会使用匿名方法来简化程序,其优点是简洁、其缺点是难以复用。
所谓匿名方法,就是没有名字的方法体,他是通过委托来完成。直接在委托后面写程序块作为方法体。
public event Action<int, int> MyEvent; //定义事件 MyEvent += delegate(int a, int b){ a++; a = a + b; }; //匿名方法通过委托绑定事件
·Lambda表达式
Lambda表达式可以简化匿名方法传给委托变量。
我们先看一段代码
public event Action<int, int> MyEvent; MyEvent += (a, b) => { a++; a = a + b; };
和匿名方法一样,只是在参数与方法体之间,加多了一个 “=>” 表示为Lambda表达式,而省略了委托关键字和参数类型定义(可省略)。看起来是不是简洁了许多。
·输入参数格式
·返回值格式
隐式返回值类型:通过表达式结果(或者语句块返回值)的类型来确定其(匿名函数)返回值类型。
当符号右侧只有一条表达式的情况下,返回值为表达式的结果,编译器会自动识别其结果的类型,如:
Func<int, int, bool> testForEquality = (x, y) => x == y;
上面代码声明了一个委托变量testForEquality,其签名为:两个int类型的参数、返回值为bool,当赋值给testForEquality时,函数签名必须匹配。
将Lambda表达式(x, y) => x == y赋给委托,表达式的结果也是bool类型,符合签名。
以下代码编译会报错:“无法将 lambda 表达式 转换为预期委托类型,因为块中的某些返回类型不可隐式转换为委托返回类型 ”,因为签名不一样。
Func<int, int, int> testForEquality = (x, y) => x == y;
当符号右侧是语句块时,如果需要返回值,必须有return,如:
Func<int, int, int> testForEquality = (x, y) => {x += x; y += y;return x + y;}
当然我们还可以显式声明返回值类型,格式如下:
(input-parameters) =>(return-type){ statements }
“return-type” 就是匿名函数的类型,好比声明普通函数时的返回值类型
以上部分内容摘取官方文档:Lambda 表达式 - C# 引用 | Microsoft Docs
建议大家多看看官方文档,确实总结的很好。
四、Lambda 用于 Expression-bodied (成员的实现)
通过表达式主体定义,可采用非常简洁的可读形式提供成员的实现。 只要任何支持的成员(如方法或属性)的逻辑包含单个表达式,就可以使用表达式主体定义。
·方法
expression-bodied 方法包含单个表达式,它返回的值的类型与方法的返回类型匹配;或者,对于返回 void
的方法,其表达式则执行某些操作。
public override string ToString() => "我已经被重写了!"; string overResual = ToString();
监听结果如下:
·只读属性
从 C# 6 开始,可以使用表达式主体定义来实现只读属性。 为此,请使用以下语法:
PropertyType PropertyName => expression;
下面的示例定义 Location
类,其只读 Name
属性以表达式主体定义的形式实现,该表达式主体定义返回私有 locationName
字段值:
public class Location { private string locationName; public Location(string name) { locationName = name; } public string Name => locationName; }
·属性
从 C# 7.0 开始,可以使用表达式主体定义来实现属性 get
和 set
访问器。 下面的示例演示其实现方法:
class HIKSDK:CameraSDKBase { private static HIKSDK _instance; //单例对象 internal static HIKSDK Instance { get => _instance == null ? _instance = new HIKSDK(string.Empty) : _instance; set => _instance = value; } }
上面例子结合了单例模式和lambda属性,get 访问器的表达式 ”_instance == null ? _instance = new HIKSDK(string.Empty) : _instance“ 表示判断instance是否为null,如果是,就new一个对象并返回,否则直接返回。
这里说明一下,”?“运算符是可以返回一个等式的,如: int result = flag ? parm = 1 : parm = 0; 其结果和int result = flag ? 1 : 0;是一样的。
·构造函数
构造函数的表达式主体定义通常包含单个赋值表达式或一个方法调用,该方法调用可处理构造函数的参数,也可初始化实例状态。
以下示例定义 Location
类,其构造函数具有一个名为“name”的字符串参数。 表达式主体定义向 Name
属性分配参数。
public class Location { private string locationName; public Location(string name) => Name = name; public string Name { get => locationName; set => locationName = value; } }
·终结器
终结器的表达式主体定义通常包含清理语句,例如释放非托管资源的语句。
下面的示例定义了一个终结器,该终结器使用表达式主体定义来指示已调用该终结器。
using System; public class Destroyer { public override string ToString() => GetType().Name; ~Destroyer() => Console.WriteLine($"The {ToString()} finalizer is executing."); }
·索引器
与使用属性一样,如果 get
访问器包含返回值的单个表达式或 set
访问器执行简单的赋值,则索引器 get
和 set
访问器包含表达式主体定义。
下面的示例定义名为 Sports
的类,其中包含一个内部 String 数组,该数组包含大量体育运动的名称。 索引器的 get
和 set
访问器都以表达式主体定义的形式实现。
using System; using System.Collections.Generic; public class Sports { private string[] types = { "Baseball", "Basketball", "Football", "Hockey", "Soccer", "Tennis", "Volleyball" }; public string this[int i] { get => types[i]; set => types[i] = value; } }
以上内容大部分摘取官方文档: 表达式主体定义的成员 - C# 编程指南 | Microsoft Docs
五、LINQ结合lambda表达式
由于篇幅问题,这里会单独再开一章节讲