在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表达式与匿名方法的更多相关文章

  1. 委托学习过程及委托、Lambda表达式和匿名方法的关系总结及事件总结

    第一章,当开始学习委托的时候,我们会问什么是委托?为什么要学习委托? 一,什么是委托? 委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法, ...

  2. 匿名函数:Lambda表达式和匿名方法

    匿名函数一个"内联"语句或表达式,可在需要委托类型的任何地方使用.可以使用匿名函数来初始化命名委托,或传递命名委托(而不是命名委托类型)作为方法参数. 共有两种匿名函数: Lamb ...

  3. Effective Java 第三版——42.lambda表达式优于匿名类

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  4. kotlin之lambda表达式和匿名函数

    lambda表达式,称为匿名函数,是一种函数字面值,也就是没有声明的函数,但可以作为表达式传递出去. 函数类型: 对于接受另一个函数的作为自己的参数,必须针对这个参数指定一个函数的类型如 fun &l ...

  5. Python函数与lambda 表达式(匿名函数)

    Python函数 一.函数的作用 函数是组织好的,可重复使用的,用来实现单一或相关联功能的代码段 函数能提高应用的模块性和代码的重复利用率 python 内置函数:https://docs.pytho ...

  6. Java8函数式接口/Lambda表达式/接口默认方法/接口静态方法/接口冲突方法重写/lambda表达式指定泛型类型等

    一:函数式接口 1.函数式接口的概念就是此接口必须有且只能有一个抽象方法,可以通过@FunctionalInterface来显示规定(类似@Override),但是没有此注解的但是只有一个抽象方法的接 ...

  7. Java 8新特性探究(一) JEP126特性lambda表达式和默认方法

    Lambda语法 函数式接口 函数式接口(functional interface 也叫功能性接口,其实是同一个东西).简单来说,函数式接口是只包含一个方法的接口.比如Java标准库中的java.la ...

  8. Java8 Lambda表达式实战之方法引用(一)

    方法的引用 方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法,方法引用提供了一种引用而不执行方法的方式,如果抽象方法的实现恰好可以使用调用另外一个方法来实现,就有可能可以使用方法引用 方法 ...

  9. C++11 Lambda表达式(匿名函数)

    http://www.cnblogs.com/RainyBear/p/5733399.html http://blog.163.com/lvan100@yeah/blog/static/6811721 ...

随机推荐

  1. awk 查找文件长度 删除

    #在某个目录下,由于有些是缓存文件,它们的共同点就是长度大于3, 找到它们,然后用rm 命令删除#ls abc.pyabcd.py.... #ls | awk 'length($1) > 3 { ...

  2. 泛型集合List<T> Dictionary<K,V>

    List<T>类似于ArrayList,ArrayList的升级版. 各种方法:Sort().Max().Min().Sum()…   Dictionary<K,V>类似于Ha ...

  3. PHP异常处理

    一.异常处理——可以有效地控制多条出现错误或异常的代码 基本语法如下: try{ //可能出现异常的代码 } catch(Exception $e){ //对异常处理 //1.自己处理 //2.不作处 ...

  4. Java中的TCP/UDP网络通信编程

    127.0.0.1是回路地址,用于测试,相当于localhost本机地址,没有网卡,不设DNS都可以访问. 端口地址在0~65535之间,其中0~1023之间的端口是用于一些知名的网络服务和应用,用户 ...

  5. STM32之SD卡

    目录 一.SD卡概述 1.定义 2.容量等级 3.SD卡框图 4.SD卡与TF卡的区别 二. SD卡内部结构 1. SD卡内部结构简图 2. 存储阵列结构图 3.Buffer 4.“存储阵列Block ...

  6. Controllers, Actions 和 Action Results

    Controllers, Actions 和 Action Results 原文:Controllers, Actions, and Action Results作者:Steve Smith翻译:姚阿 ...

  7. CSS标签居中

    CSS标签居中是相对于父标签说的,即在父标签的中居中.通常是在子标签中使用margin:0 auto,来使子标签居中.此外子标签需要有固定的宽度才行,比如 子标签为div时,div的宽度默认占父标签的 ...

  8. 【C#】动态加载dll程序集

    原文:http://www.cnblogs.com/bomo/archive/2013/03/01/2938165.html 很多时候我们需要用到引用其他程序集,有些程序集是.Net生成的,可以支持反 ...

  9. bzoj 1200: [HNOI2005]木梳 DP

    1200: [HNOI2005]木梳 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 266  Solved: 125[Submit][Status] ...

  10. 把 Eclipse 中的工程 Push 到 Github(适用 Windows 平台)

    今天发现一小技巧,关于如何把Eclipse的某一个Existing project push 到github服务器. Eclipse 应该是 JavaEE 版本. 在project 右键 team, ...