欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

委托发展史(三)

程序员文章站 2022-06-25 16:05:05
通过C#2极大的简化了委托的使用。如果仅仅是为了简化事件的订阅以及增强可读性,这些技术确实已经足够了。 但是,C#2中的委托仍然过于臃肿:一页充满匿名方法的代码,读起来真让人难受,你也肯定不愿意经常在一个语句中放入多个匿名方法吧。 C#3可以说是一个工业革命。 *作为委托的Lambda表达式 从许多 ......

通过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语法简写总结

委托发展史(三)