Lambda表达式与匿名方法
在C#2中,由于有了方法组,匿名方法,类型的协变和抗变,使得运用delegate变得很容易,在注册事件时代码变得简单易读,但是在C# 2中,代码仍然有点臃肿,大块的匿名方法会降低代码的可读性,一般我们不会在一条语句中写多个匿名方法。
LINQ产生的一个目的是能够方便的对数据进行管道操作而不失语义。LINQ能够表达对数据进行的各种逻辑操作,LINQ执行时,这些操作实际上都是通过委托来实现的。使用LINQ to Object操作数据时,一条语句中包含多个委托是很常见的,C# 3中的Lambda表达式正式这幕后的功臣,它不仅使得我们能够在一条代码中写多个委托,而且不会丧失代码的可读性。相信用过LINQ的人应该都有这样的体会。
在很多方面Lambda表达式可以看成是C#2中匿名方法的演变。他们俩的功能是一样的,都使得代码更加清晰和紧凑。另外,Lambda表达式和匿名方法在闭包的特性上是一致的,但是Lambda表达式还有一些小的特性,能够使得代码在大多数情况下更加紧凑。和匿名方法一样,Lambda表达式有着自己的转换规则---表达式的类型并不是一个委托的类型,但是它可以显示或者隐式的转换为一个委托的实例。
下面来看看Lambda表达式是如何来表示委托的。我们先从一个简单的例子开始。首先我们写一个以String类型为参数,返回Int32类型的值的委托实例。然后演示委托如何一步一步转换为Lambda表达式。
一、Func<……>泛型委托类型
首先我们要选委托类型,在.NET 3.5中,提供了一系列泛型委托类型。在.NET3.5中有5个名为Func的泛型委托类型。每一个类型里面包含0至4个类型参数。5个Func泛型类型签名如下:
TResult Func<TResult>()
TResult Func<T,TResult>(T arg)
TResult Func<T1,T2,TResult>(T1 arg1, T2 arg2)
TResult Func<T1,T2,T3,TResult>(T1 arg1, T2 arg2, T3 arg3)
TResult Func<T1,T2,T3,T4,TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
尖括号中的,都是类型参数,例如,Func<String,Double,Int32> 表示该委托接受两个参数,第一个是String,第二个是Double,返回Int32类型,所有适合该类型的方法都可以使用该委托。Func<String,Double,Int32>等价如下面的委托:
public delegate Int32 SomeDelegate(String arg1, Double arg2)
.NET 3.5中还有一个名为Action<……>的泛型委托类,他的用法和Func的差不多,只是他的返回值为void。如果您觉得5个参数不够用的话,在.NET 4.0中,Action<……>和Func<……>的参数扩展到了16个,如Func<T1,……,T16,TResult>,这么多的参数主要是为了支持DLR。
在我们这个例子中,我们需要一个类型,他接受String作为参数,返回Int32类型的值,所以我们可以使用Func<String,Int32>。
二、转化为Lambda表达式的第一步
根据上面的委托类型,我们可以使用匿名方法创建一个委托实例。如下,该实例返回String参数的长度。
Func<String, Int32> returnLength;
returnLength = delegate(String text) { return text.Length; };
Console.WriteLine(returnLength("Hello"));
上面的例子中,将输出5. 上面的以delegate开头的是匿名表达式。现在我们开始对这部分进行转换。最常见的Lambda表达式的形式如下:
(参数类型1 参数名1 [ , 参数类型2 参数名2 ……]) => { 方法内部表达式 }
=>是在C# 3中引入的,他告诉编译器,我们将使用Lambda表达式,大多数的使用Lambda表达式代表委托的方法都有一个返回值。但在C# 1中,委托通常用于事件,所以很少有返回值。在LINQ中,委托用做数据管道的一部分,使得数据能够进行各种映射,过滤等操作。
下面我们使用Lambda表达式来表示该委托的实例,代码如下:
Func<String, Int32> returnLength;
returnLength = (String text)=> { return text.Length; };
Console.WriteLine(returnLength("Hello"));
上面的例子返回的结果和之前的一样,在大括号中,我们可以写任意表达式,只要返回值为String类型。
三、使用单个表达式作为函数体
前面的例子中,我们使用一对大括号将返回值表达式括起来了。这样做很灵活,你可以在括号内写多条语句,进行各种操作,就像在匿名方法中的那样。但是大多数情况下,通常可以将这个函数体表示为单个表达式,这个表达式的值就是Lambda表达式的值。在这种情况下,我们可以省去左右的大括号和逗号,表达式样子下面的样子。
(参数类型1 参数名1 [ , 参数类型2 参数名2 ……]) =>方法表达式
这样,上面例子中的右边部分可以改写为:
returnLength = (String text) => text.Length;
现在看起来简洁多了。那参数类型怎么办呢,由于编译器已经知道他是Func<String,Int32>的一个实例,该实例接受一个String类型的参数,所以我们可以直接将参数卸载括号内,从而省略参数类型。
四、隐式类型参数列表
在大多数情况下,编译器能够推断出参数的类型,而不需要我们现实的去声明,因此,Lambda表达是可以改写为:
(参数名1 [ ,参数名2 ……]) =>方法表达式
隐式类型参数列表,就是一系列用逗号分隔的参数名称。这些参数类型,要么全部都显示声明类型,要么全部不声明类型让编译器推断,不能一部分显示声明,一部分隐式声明。如果有参数类型为out或者in的话,就必须显示声明参数类型。这样我们的Lambda表达式可以改写为:
returnLength = (text) => text.Length;
现在看起来简洁多了。最后唯一一点有点不爽的是,参数有个括号。
五、去掉单参数两侧的括号
当Lambda表达式只有一个参数的额时候,C# 3运行我们省略掉两边的括号,整个Lambda表达式变为下面的形式:
参数名 =>方法表达式
根据这一规则,之前例子中的Lambda表达个可以改为:
returnLength = text => text.Length;
你可能会想,哪有这么多特例啊,参数恰好只有一个,函数主题恰好能用一个表达式表达完。这里是为了展示Lambda表达式如何简化代码提高可读性,很多时候,当大量的这样的情况出现时,使用Lambda表达式能够极大提高代码可读性。现在整个方法可以写成下面这样:
Func<String, Int32> returnLength;
returnLength = text => text.Length;
Console.WriteLine(returnLength("Hello"));
现在看起来简洁多了,可能第一次觉得这样写怪怪的,就像第一次用匿名方法和LINQ那样,用久了就习惯了。当使用Lambda表达式时,你能够体会到在创建一个委托实例时时多磨的简洁。上面例子中,我们可以把变量text换乘其他名字比如x,在LINQ中经常这样,但是长一点的变量可以更加容易阅读。下图总结了以上的步骤:

Lambda表达式与匿名方法的更多相关文章
- 委托学习过程及委托、Lambda表达式和匿名方法的关系总结及事件总结
第一章,当开始学习委托的时候,我们会问什么是委托?为什么要学习委托? 一,什么是委托? 委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法, ...
- 匿名函数:Lambda表达式和匿名方法
匿名函数一个"内联"语句或表达式,可在需要委托类型的任何地方使用.可以使用匿名函数来初始化命名委托,或传递命名委托(而不是命名委托类型)作为方法参数. 共有两种匿名函数: Lamb ...
- Effective Java 第三版——42.lambda表达式优于匿名类
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- kotlin之lambda表达式和匿名函数
lambda表达式,称为匿名函数,是一种函数字面值,也就是没有声明的函数,但可以作为表达式传递出去. 函数类型: 对于接受另一个函数的作为自己的参数,必须针对这个参数指定一个函数的类型如 fun &l ...
- Python函数与lambda 表达式(匿名函数)
Python函数 一.函数的作用 函数是组织好的,可重复使用的,用来实现单一或相关联功能的代码段 函数能提高应用的模块性和代码的重复利用率 python 内置函数:https://docs.pytho ...
- Java8函数式接口/Lambda表达式/接口默认方法/接口静态方法/接口冲突方法重写/lambda表达式指定泛型类型等
一:函数式接口 1.函数式接口的概念就是此接口必须有且只能有一个抽象方法,可以通过@FunctionalInterface来显示规定(类似@Override),但是没有此注解的但是只有一个抽象方法的接 ...
- Java 8新特性探究(一) JEP126特性lambda表达式和默认方法
Lambda语法 函数式接口 函数式接口(functional interface 也叫功能性接口,其实是同一个东西).简单来说,函数式接口是只包含一个方法的接口.比如Java标准库中的java.la ...
- Java8 Lambda表达式实战之方法引用(一)
方法的引用 方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法,方法引用提供了一种引用而不执行方法的方式,如果抽象方法的实现恰好可以使用调用另外一个方法来实现,就有可能可以使用方法引用 方法 ...
- C++11 Lambda表达式(匿名函数)
http://www.cnblogs.com/RainyBear/p/5733399.html http://blog.163.com/lvan100@yeah/blog/static/6811721 ...
随机推荐
- C++里容易忽视却不能忽视的
1 define 只是简单地文本替换. 2 每个机器的字长不同. 3 每个类型在不同的机器上,所占用的内存空间不同. 4 每个机器内部的字节大小端不同. 5 并不是所有的编译器或机器都支持最新的C++ ...
- Kali linux网络配置
Kali linux 安装完成后,需要对其网络进行配置.使用DHCP服务是配置网卡最简单的方法之一,但渗透测试时通常不会这样做,因为系统会被记录在DHCP服务器的数据库中. 1 动态DHCP方式 配 ...
- php PHP_EOL 常量
换行符 unix系列用 \n windows系列用 \r\n mac用 \r PHP中可以用PHP_EOL来替代,以提高代码的源代码级可移植性 <?php echo PHP_EOL; //win ...
- 简易的RestClient代码
package tests; import java.io.*; import org.apache.http.HttpEntity; import org.apache.http.HttpRespo ...
- yii2源码学习笔记(十四)
Module类是模块和应用类的基类. yiisoft\yii2\base\Module.php <?php /** * @link http://www.yiiframework.com/ * ...
- uboot顶层config.mk分析
uboot顶层目录中的config.mk定义了确定了当前执行makefile所对应的源文件目录.目标文件目录,编译的程序编译.连接的选项,以及目标文件生成的规则等等.它被包含在顶层的makefile以 ...
- Clang 与 LLVM
我们在iOS调试中经常会看到Clang这个,那么Clang到底是什么呢?我们来简单了解一下. Clang是一个C.C++.OC语言的轻量级编译器.源代码发布于BSD协议下.Clang是由C++编写,基 ...
- 教你在你的应用程序中扩展使用dynamic类型
教你在你的应用程序中扩展使用dynamic类型 相信大家在开发中经常会接触到mvc中的ViewBag,有心的同学会,发现这就是一个dynamic类型,查看源码一谈究竟,本文也是根据dynamic来扩展 ...
- 第 9 章 模板方法模式【Template Method Pattern】
以下内容出自:<<24种设计模式介绍与6大设计原则>> 周三,9:00,我刚刚坐到位置,打开电脑准备开始干活. “小三,小三,叫一下其它同事,到会议室,开会”老大跑过来吼,带着 ...
- uva 1449 - Dominating Patterns
简单的AC自动机: #include<cstdio> #include<cstring> #include<queue> #define maxn 150005 u ...