通過C#2極大的簡化了委托的使用。如果僅僅是為了簡化事件的訂閱以及增強可讀性,這些技術確實已經足夠了。
但是,C#2中的委托仍然過於臃腫:一頁充滿匿名方法的代碼,讀起來真讓人難受,你也肯定不願意經常在一個語句中放入多個匿名方法吧。
C#3可以說是一個工業革命。
從許多方面Lambda表達式都可以看做是C#2的匿名方法的一種演變。
匿名方法能做到的幾乎一切事情都可以用Lambda表達式來完成,另外,幾乎所有情況下,Lambda表達式都更易讀,更緊湊。
從最顯而易見的方面看,兩者並無多大區別--只是Lambda支持許多簡化語法使他們在常規條件下顯得更簡練。
與匿名方法相似,Lambda表達式有特殊的轉換規則:表達式的類型本身並非委托類型,但它可以通過多種方式隱式或顯式轉換成一個委托實例。
匿名函數這個術語同時涵蓋了匿名方和Lambda表達式--,在很多情況下,兩者可以使用相同的轉換規則。
慢慢來看這一場工業革命吧。。。
在.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>。
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表達式之后,你一定會感慨他們是多么的簡潔,很難想象還可以使用更短,更清晰的方式老創建委托實例。
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。