委托發展史(三)


通過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語法簡寫總結

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



委托發展史(Linq操作符) 對數的發展史 移動通信的發展史 簡述 MySQL 發展史 program發展史及 forecast Flash與Flex的發展史 C語言發展史(二) SUN公司發展史 淺淡瀏覽的發展史 虛擬化的發展史
 
粤ICP备14056181号  © 2014-2021 ITdaan.com