通过C#2极大的简化了委托的使用。如果仅仅是为了简化事件的订阅以及增强可读性,这些技术确实已经足够了。
但是,C#2中的委托仍然过于臃肿:一页充满匿名方法的代码,读起来真让人难受,你也肯定不愿意经常在一个语句中放入多个匿名方法吧。
C#3可以说是一个工业革命。
*作为委托的Lambda表达式
从许多方面Lambda表达式都可以看做是C#2的匿名方法的一种演变。
匿名方法能做到的几乎一切事情都可以用Lambda表达式来完成,另外,几乎所有情况下,Lambda表达式都更易读,更紧凑。
从最显而易见的方面看,两者并无多大区别--只是Lambda支持许多简化语法使他们在常规条件下显得更简练。
与匿名方法相似,Lambda表达式有特殊的转换规则:表达式的类型本身并非委托类型,但它可以通过多种方式隐式或显式转换成一个委托实例。
匿名函数这个术语同时涵盖了匿名方和Lambda表达式--,在很多情况下,两者可以使用相同的转换规则。
慢慢来看这一场工业革命吧。。。
*Func<...>委托类型简介
在.NET3.5的System命名空间中,有5个泛型Func委托类型,
Func并无特别之处——只是他提供了一些好用的预定义泛型类型,在很多情况下能帮助我们处理问题。
每个委托签名都获取0~4个参数,其类型用类型参数来指定。最后一个类型参数用作每种情况下的返回类型。
通俗讲就是这个Func是一个有返回值委托类型。
看一下.NET3.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,int> 等价于以下形式的委托类型。
public delegate int TestDelegate(string arg1, double arg2);
当你想返回void时,也就是无返回值,可使用Action<...>系列委托,其功能相似。
Action在.Net2.0中就有了,但其他都是.NET 3.5新增的。如果4个参数还嫌不够,.NET 4将Action与Func家族扩展为拥有16个参数。
因此Func<T1,..., T16 , TResult >拥有17个参数类型。
例如,我们需要获取一个stirng参数,并返回一个int,所以我们将使用Func<string,int>。
*转换到Lambda表达式
Func<string, int> returnLength; returnLength = delegate (string text) { return text.Length; }; Console.WriteLine(returnLength("Hellow"));
最后会输出"5",预料之中。
注意上面的代码,returnLength的声明与赋值是分开的,否则一行可能放不下——除此之外,这样还有利于对代码的理解。所以,我们将它转换成Lambda表达式
Lambda表达式最冗长的形式是:
(显式类型的参数列表) => { 语句 }
=>这个是C#3新增的,他告诉编译器我们正在使用一个Lambda表达式。Lambda表达式大多数时候都和一个返回非void的委托类型配合使用——如果不反悔一个结果,语法就不像现在这样一目了然。
这个版本包含显式参数,并将语句放到大括号中,他看起来和匿名方法非常相似。
Func<string, int> returnLength; returnLength = (string text) => { return text.Length; }; Console.WriteLine(returnLength("Hellow"));
在阅读Lambda表达式时,可以将=>部分看成"goes to"。
匿名方法中控制返回语句的规则同样适用与Lambda表达式:不能从Lambda表达式返回void类型;
如果有一个非void的返回类型,那么每个代码路径都必须返回一个兼容的值。
到目前为止,使用Lambda表达式并没有节省多大空间,或使代码变得容易阅读。
*用单一表达式作为主体
我们目前使用一个完整的代码块来返回值,这样可以灵活地处理多种情况——可以在代码块中放入多个语句,可以执行循环,可以从代码块中不同位置返回。。。等等
这和匿名方法是一样的。
然而,大多数时候,都可以用一个表达式来表示整个主体,该表达式是Lambda结果。(意思就是,一条语句就可以解决的事)
这些情况下,可以指定那个表达式,不用大括号;不使用return语句,也不添加分号,格式随即变成:
(显式类型的参数列表) => 表达式
在我们的例子中,就变成了——
(string text) => text.Length
现在已经开始变得简单了,接着来考虑一下参数类型。编译器已经知道Func<string,int>的实例获取单个字符串,所以只需命名那个参数就可以了。
感觉还是得分两行来声明跟赋值啊。。。
*隐式类型的参数列表
编译器大多数时候都能猜出参数类型,不需要你显示声明他们。这些情况下,
还可以更加简便些。
(隐式类型的参数列表) => 表达式
嗯,更加简便了,Lambda表达式也变成了这样:
(text) => text.Length
隐式类型的参数列表就是一个以逗号分隔的名称列表,没有类型。但是隐式和显式类型的参数不能混合匹配——要么整个列表都是显式类型,要么都是隐式类型。
如果存在out 或 ref参数,那么就只能是显式类型了。
上面的Lambda表达式已经相当简短了,可以继续简化的地方不多了。
哎~这个圆括号看起来有点多余啊。除去它!
*单一参数的快捷语法
如果Lambda表达式只需要一个参数,而且那个参数可以隐式指定类型,C#3就允许省略圆括号。这种Lambda表达式是:
参数名 => 表达式
因此我们的Lambda表达式最终形式是:
text => text.Length
这样的话如果一小段代码中含有多个Lambda,那么拿掉参数列表的圆括号之后,对于可读性来说是增强不少的。
还有如果愿意,可以用圆括号将整个Lambda表达式括起来。
在大多数情况下这种形式都是十分易读的,例如之前的例子写出来就是这样:
Func<string, int> returnLength; returnLength = text => text.Length; Console.WriteLine(returnLength("Hellow"));
可能刚开始读起来有点"别扭",不过很快就习惯啦~
当你习惯了Lambda表达式之后,你一定会感慨他们是多么的简洁,很难想象还可以使用更短,更清晰的方式老创建委托实例。
*Lambda语法简写总结