[.net 面向?qū)ο蟪绦蛟O(shè)計(jì)進(jìn)階] (5) Lamda表達(dá)式(一) 創(chuàng)建委托
本節(jié)導(dǎo)讀:
通過學(xué)習(xí)Lambda表達(dá)式,學(xué)會(huì)創(chuàng)建委托和表達(dá)式目錄樹,深入了解Lambda的特性,讓你的代碼變的更加清晰、簡(jiǎn)潔、高效。
讀前必備:
本節(jié)學(xué)習(xí)前,需要掌握以下知識(shí):
A.泛型 (請(qǐng)參考[.net 面向?qū)ο缶幊袒A(chǔ)] (18) 泛型)
B.Linq基礎(chǔ)(請(qǐng)參照[.net 面向?qū)ο缶幊袒A(chǔ)] (19)LINQ基礎(chǔ))
C.Linq使用(請(qǐng)參照[.net 面向?qū)ο缶幊袒A(chǔ)](20)LINQ使用)
D.委托 (請(qǐng)參照[.net 面向?qū)ο缶幊袒A(chǔ)] (21) 委托)
E.事件 (請(qǐng)參照[.net 面向?qū)ο缶幊袒A(chǔ)] (22) 事件)
通過《.net面向?qū)ο缶幊袒A(chǔ)》系列中相關(guān)介紹,我們已經(jīng)初步使用過了Lambda表達(dá)式進(jìn)行Linq查詢,這節(jié)我們主要深入了解Lambda表達(dá)式。
1. 關(guān)于Lambda
Lambda表達(dá)式是一種可用于創(chuàng)建委托或表達(dá)式目錄樹類型的匿名函數(shù)。
以上是微軟對(duì)Lambda表達(dá)式的定義。從這個(gè)定義中,我們可以看出,Lambda的存在,主要做兩件事:
A.創(chuàng)建委托(Delegate)
B.創(chuàng)建表達(dá)式樹(ExPRessionTree)
此外,Lambda表達(dá)式的本質(zhì)就是匿名方法(或叫匿名函數(shù))。
后面面我們分別從這兩個(gè)方法入手,進(jìn)一步學(xué)習(xí)Lambda帶我給我們的便利。
2.Lambda表達(dá)式
Lambda表達(dá)式的構(gòu)成如下:
零個(gè)參數(shù):()=>expr
一個(gè)參數(shù):(param)=>expr或param=>expr
多個(gè)參數(shù):(param-list)=>expr
所有Lambda表達(dá)式都使用Lambda運(yùn)算符=>,該運(yùn)算符讀作"goesto"
當(dāng)參數(shù)只有一個(gè)時(shí),右邊的括號(hào)可以省略。
下面是寫法舉例:
//零個(gè)參數(shù)() => MethodName()//一個(gè)參數(shù)(x) => x+xX=>x+x//兩個(gè)及以上參數(shù)(m,n) => m.Length>n//顯式類型參數(shù)(int x,string y)=>y.Length>x
上面的示例中,其中顯式類型參數(shù),是當(dāng)編譯器無法推斷其參數(shù)類型時(shí),可以顯式的定義參數(shù)類型。
3.Lambda語(yǔ)句
Lambda語(yǔ)句和Lambda表達(dá)式類似,只是右邊部分寫在{}中
Lambda語(yǔ)句構(gòu)成如下:
(inputparameters)=>{statement;}
示例:
delegate void TestDelegate(string s);…TestDelegate myDel = n => { string s = n + " " + "World"; Console.WriteLine(s); };myDel("Hello");
4.異步Lambda(async和await)
我們通過在《.net面向?qū)ο缶幊袒A(chǔ)》中學(xué)習(xí),了解了事件本身也是一種特殊的委托,那么Lambda可用于創(chuàng)建委托也就是說同樣可以來創(chuàng)建事件。
下面看示例:
首先看一個(gè)普通按鈕點(diǎn)擊事件的實(shí)現(xiàn)及Lambda語(yǔ)句寫法
下面是普通寫法:
this.myButton.Click += new System.EventHandler(this.myButton_Click);//事件通常委托寫法private void myButton_Click(object sender, EventArgs e){ MessageBox.Show("您好,我是按鈕點(diǎn)擊事件!");}下面是Lambda寫法:
//事件Lambda語(yǔ)句寫法myButton.Click += (sender, e) => { MessageBox.Show("您好,我是按鈕點(diǎn)擊事件!這是Lambda語(yǔ)句寫法"); };上面兩種寫法,我們?cè)谇懊娴恼鹿?jié)中已經(jīng)有很多例子了。
下面我們看一下異步的事件:
//異步事件普通寫法private async void asyncButton_Click(object sender, EventArgs e){ await MyMethodAsync(); MessageBox.Show("您好,我是按鈕點(diǎn)擊事件!異步的喲!");}async Task MyMethodAsync(){ await Task.Delay(1000);}我們用Lambda來改寫上面的異步事件:
//異步事件Lambda寫法asyncButton.Click += async (sender, e) => { await MyMethodAsync(); MessageBox.Show("您好,我是按鈕點(diǎn)擊事件!異步的喲!這是Lambda語(yǔ)句寫法"); };async Task MyMethodAsync(){ await Task.Delay(1000);}上面的異步事件,就是在事件委托階段使用async來表示這是一個(gè)異步的,在事件處理中階段使用await關(guān)鍵詞來指定一個(gè)異步方法。
Lambda語(yǔ)句寫法同樣是針對(duì)異步事件的簡(jiǎn)潔寫法,具有相同的效力。
5. 標(biāo)準(zhǔn)查詢運(yùn)算的Lambda表達(dá)式
5.1泛型委托使用Lambda
我們?cè)赱.net 面向?qū)ο缶幊袒A(chǔ)] (21) 委托一節(jié)中說到了三種常用的泛型委托
Action(無返回值泛型委托)
Func(有返回值泛型委托)
predicate(返回值為bool型的泛型委托)
這節(jié)不再重復(fù)說明泛型委托,不熟悉泛型委托的小伙伴,請(qǐng)參考,我們只舉例說明泛型委托和它的Lambda寫法
//Action 無返回值類型的 泛型委托//匿名方法聲明及調(diào)用Action<int, int> act = delegate (int a, int b) { Console.WriteLine(a + "+" + b + "=" + (a + b));};act(11, 22);//表達(dá)式聲明及調(diào)用Action<int, int> actLambda = (a, b) => { Console.WriteLine(a + "+" + b + "=" + (a + b)); };actLambda(111, 222);//Func 帶返回值的 泛型委托//匿名方法聲明及調(diào)用Func<int, int, string> acc = delegate (int a, int b) { return (a + "+" + b + "=" + (a + b));};Console.WriteLine(acc(11, 22));//表達(dá)式聲明及調(diào)用Func<int, int, string> ac = (a, b) => { return (a + "+" + b + "=" + (a + b)); };Console.WriteLine(ac(111, 222));5.2Linq中使用Lambda
關(guān)于在Linq中使用Lambda表達(dá)式,我們?cè)赱.net 面向?qū)ο缶幊袒A(chǔ)](20)LINQ使用有詳細(xì)說明了,不熟悉的小伙伴請(qǐng)參考,下面我們舉例說明幾種常用的Lambda查詢寫法。
5.2.1Count求數(shù)量
//查詢下列數(shù)中的奇數(shù) - Lambda寫法int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };int oddNumbers = numbers.Count(n => n % 2 == 1);Console.WriteLine("奇數(shù)有:"+oddNumbers.ToString()+ "個(gè),Lambda寫法");//查詢下列數(shù)中的奇數(shù) -Linq查詢寫法var oddNumbersFunc = (from num in numbers where num % 2 == 1 select num ).Count();Console.WriteLine("奇數(shù)有:" + oddNumbersFunc.ToString() + "個(gè),Linq查詢寫法");運(yùn)行結(jié)果如下:

5.2.2 TakeWhile滿足條件就返回集合
//TakeWhile只要滿足指定的條件就返回,在檢檢到number中的6時(shí),就返回,其中8 和9 不滿足條件,則返回 5 4 1 3 int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };var firstNumbersLessThan6 = numbers.TakeWhile(n => n < 6);Console.WriteLine("滿足條件就返回的數(shù)有:" + firstNumbersLessThan6.Count().ToString() + "個(gè),Lambda寫法");//第二個(gè)參數(shù)index表示n的索引位置string NewNumber = String.Empty;numbers.TakeWhile((n, index) => n >= index).ToList().ForEach(m=> NewNumber+=m +" ");Console.WriteLine("滿足條件就返回的數(shù)有:" + NewNumber + ",Lambda寫法");運(yùn)行結(jié)果如下:

6. Lambda的類型推理
在編寫lambda時(shí),通常不必為輸入?yún)?shù)指定類型,因?yàn)榫幾g器可以根據(jù)lambda主體、參數(shù)的委托類型以及C#語(yǔ)言規(guī)范中描述的其他因素來推斷類型。對(duì)于大多數(shù)標(biāo)準(zhǔn)查詢運(yùn)算符,第一個(gè)輸入是源序列中的元素類型。因此,如果要查詢IEnumerable<Customer>,則輸入變量將被推斷為Customer對(duì)象,這意味著你可以訪問其方法和屬性:
//Lambda的類型推理List<MyClass> list = new List<MyClass>() { new MyClass(){ at1 = "aaa", at2 = 2, at3 = DateTime.Now}, new MyClass{ at1 = "bbb", at2 = 5, at3 = DateTime.Parse("2015-06-07") }, new MyClass{ at1 = "aaa", at2 = 1, at3 = DateTime.Parse("2010-11-12") }}; Console.WriteLine("at1為aaa的元素有:" + list.Where(m => m.at1 == "aaa").Count() + "個(gè),Lambda寫法");class MyClass { public string at1 { get; set; } public int at2 { get; set; } public DateTime at3 { get; set; }}運(yùn)行結(jié)果如下:

Lambda的一般規(guī)則如下:
A.Lambda包含的參數(shù)數(shù)量必須與委托類型包含的參數(shù)數(shù)量相同。
B.Lambda中的每個(gè)輸入?yún)?shù)必須都能夠隱式轉(zhuǎn)換為其對(duì)應(yīng)的委托參數(shù)。
C.Lambda的返回值(如果有)必須能夠隱式轉(zhuǎn)換為委托的返回類型。
請(qǐng)注意,lambda表達(dá)式本身沒有類型,因?yàn)槌R?guī)類型系統(tǒng)沒有“Lambda表達(dá)式”這一內(nèi)部概念。但是,有時(shí)以一種非正式的方式談?wù)搇ambda表達(dá)式的“類型”會(huì)很方便。在這些情況下,類型是指委托類型或lambda表達(dá)式所轉(zhuǎn)換到的 Expression類型。
7. Lambda表達(dá)式的變量范圍
我們?cè)赱.net 面向?qū)ο缶幊袒A(chǔ)] (19)LINQ基礎(chǔ)一節(jié)中說到匿名方法時(shí)提到匿名方法可以引用滿園內(nèi)的外部變量,前面示例中也有提及。
下面我們看一個(gè)示例:
class Program{ static int num = 2; static void Main(string[] args) { int num2 = 7; //Lambda表達(dá)式的變量范圍 //相對(duì)以下表達(dá)式內(nèi)部,一個(gè)類的字段num和一個(gè)變量num2,下面測(cè)試在表達(dá)式內(nèi)部調(diào)用 int[] numbers2 = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; //我們對(duì)大于num小于num2的數(shù)求和 int sum = numbers2.Where(m => m > num && m < num2).Sum(); Console.WriteLine("大于num小于num2的和為:" + sum); Console.ReadKey(); }}下列規(guī)則適用于lambda表達(dá)式中的變量范圍:
A.捕獲的變量將不會(huì)被作為垃圾回收,直至引用變量的委托符合垃圾回收的條件。
B.在外部方法中看不到lambda表達(dá)式內(nèi)引入的變量。
C.Lambda表達(dá)式無法從封閉方法中直接捕獲ref或out參數(shù)。
D.Lambda表達(dá)式中的返回語(yǔ)句不會(huì)導(dǎo)致封閉方法返回。
E.如果跳轉(zhuǎn)語(yǔ)句的目標(biāo)在塊外部,則lambda表達(dá)式不能包含位于lambda函數(shù)內(nèi)部的goto語(yǔ)句、break語(yǔ)句或continue語(yǔ)句。同樣,如果目標(biāo)在塊內(nèi)部,則在lambda函數(shù)塊外部使用跳轉(zhuǎn)語(yǔ)句也是錯(cuò)誤的。
8.要點(diǎn):
本節(jié)主要說明了:
Lambda表達(dá)式在創(chuàng)建委托中的應(yīng)用;
Lambda表達(dá)式在Linq查詢中的應(yīng)用;
Lambda語(yǔ)句在異步事件中的應(yīng)用;
Lambda的兩個(gè)特性:類型推理和外部變量引用
下一節(jié),我們主要說明Lambda的另一個(gè)特點(diǎn),就是創(chuàng)建表達(dá)式目錄樹(ExpressionTree)。
==============================================================================================
返回目錄
<如果對(duì)你有幫助,記得點(diǎn)一下推薦哦,如有
有不明白或錯(cuò)誤之處,請(qǐng)多交流>
<對(duì)本系列文章閱讀有困難的朋友,請(qǐng)先看《.net面向?qū)ο缶幊袒A(chǔ)》>
<轉(zhuǎn)載聲明:技術(shù)需要共
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注