引言
在實(shí)際的項(xiàng)目中遇到一個(gè)問(wèn)題,我們經(jīng)常在網(wǎng)上搜索復(fù)制粘貼,其中有些代碼看著非常的簡(jiǎn)潔,比如Lambda表達(dá)式,但是一直沒(méi)有去深入了解它的由來(lái),以及具體的使用方法,所以在使用的時(shí)候比較模糊,其次,編程涉及面比較廣,我們不可能每個(gè)方面都去精通了解,但經(jīng)常運(yùn)到的一些東西,必須了解其具體使用方法及使用場(chǎng)景,才能書(shū)寫(xiě)出優(yōu)美、簡(jiǎn)潔、可讀性強(qiáng)的代碼。筆者通過(guò)搜索、整理資料及測(cè)試代碼,詳細(xì)的介紹Lambda 表達(dá)式的用法。
Lambda 表達(dá)式概念
“Lambda 表達(dá)式”(lambda expression)是一個(gè)匿名函數(shù),可以表示為委托的代碼,或者表示為表達(dá)式樹(shù)的代碼,它所表示的表達(dá)式樹(shù)可以編譯為委托。Lambda 表達(dá)式的特定委托類(lèi)型取決于其參數(shù)和返回值。?不返回值的 Lambda 表達(dá)式對(duì)應(yīng)于???Action???委托,具體取決于其參數(shù)數(shù)量。返回值的 Lambda 表達(dá)式對(duì)應(yīng)于Func?委托,具體取決于其參數(shù)數(shù)量。
Lambda 表達(dá)式廣泛用于:
將要執(zhí)行的代碼傳遞給異步方法,例如?Task.Run(Action)。
編寫(xiě)?LINQ 查詢表達(dá)式。
創(chuàng)建表達(dá)式樹(shù)。
C# 中委托的演變
在 C# 1.0 中,通過(guò)使用在代碼中其他位置定義的方法顯式初始化委托來(lái)創(chuàng)建委托的實(shí)例。C# 2.0 引入了匿名方法的概念,作為一種編寫(xiě)可在委托調(diào)用中執(zhí)行的未命名內(nèi)聯(lián)語(yǔ)句塊的方式。C# 3.0 引入了 Lambda 表達(dá)式,這種表達(dá)式與匿名方法的概念類(lèi)似,但更具表現(xiàn)力并且更簡(jiǎn)練。這兩個(gè)功能統(tǒng)稱為匿名函數(shù)?。通常,面向 .NET Framework 3.5 及更高版本的應(yīng)用程序應(yīng)使用 lambda 表達(dá)式。
C#1.0中委托?的實(shí)現(xiàn),代碼如下:
delegateintCalculateHandler(intx,inty);privateintSum(intx,inty){returnx + y; }publicvoidTest(){? ? CalculateHandler sumHandler =newCalculateHandler(Sum);? ? MessageBox.Show(sumHandler(1,2).ToString());//輸入結(jié)果3}
C#2.0中匿名方法?的實(shí)現(xiàn),代碼如下:
delegateintCalculateHandler(intx,inty);publicvoidTest(){? ? CalculateHandler sumHandler =delegate(intx,inty) {returnx + y; };? ? MessageBox.Show(sumHandler(1,2).ToString());//輸入結(jié)果3}
C#3.0中Lambda 表達(dá)式?的實(shí)現(xiàn),代碼如下:
delegateintCalculateHandler(intx,inty);publicvoidTest(){? ? ? CalculateHandler sumHandler = (x, y) => x + y;? ? ? MessageBox.Show(sumHandler(1,2).ToString());//輸入結(jié)果3}
由此可以看出微軟的一步步升級(jí),帶給我們的是編程上的優(yōu)美,簡(jiǎn)潔,可讀性強(qiáng),因此作為程序員我們要一直處于學(xué)習(xí)的路上。
Lambda 表達(dá)式使用
C#的Lambda 表達(dá)式都使用 Lambda?運(yùn)算符?=>,該運(yùn)算符讀為“goes to”,若要?jiǎng)?chuàng)建 Lambda 表達(dá)式,需要在 lambda 運(yùn)算符左側(cè)指定輸入?yún)?shù)(如果有),然后在另一側(cè)輸入表達(dá)式或語(yǔ)句塊。例如,單行 Lambda 表達(dá)式x => x * x?指定名為x?的參數(shù)并返回x?的平方值。
在介紹Lambda 表達(dá)式使用之前我們先了解.Net為我們定義好的Action<T>和Func<T>兩個(gè)泛型委托。
Action<T>泛型委托
Action<T>委托表示引用一個(gè)返回類(lèi)型為Void的方法。這個(gè)委托存在不同的變體,可以傳遞之多16個(gè)不同的參數(shù)類(lèi)型。同時(shí),沒(méi)有泛型參數(shù)的Action類(lèi)可以調(diào)用沒(méi)有參數(shù)的方法。例如,Action<in T>表示有一個(gè)輸入?yún)?shù)的方法,Action<in T1,in T2>表示有兩個(gè)輸入?yún)?shù)的方法。
Func<T>泛型委托
Func<T>可以以類(lèi)似的方法使用。不過(guò)Func<T>允許調(diào)用帶返回參數(shù)的方法。Func<T>也有不同的變體,之多可以傳遞16個(gè)參數(shù)和一個(gè)返回類(lèi)型。例如:Func<out TResult>委托類(lèi)型可以無(wú)參的帶返回類(lèi)型的方法,F(xiàn)unc<in T1,inT2,out Tresult>表示帶兩個(gè)參數(shù)和一個(gè)返回類(lèi)型的方法。
Func可以表示帶輸出的方法,T可以有多個(gè),且只有最后一個(gè)表示輸出?即最后一個(gè)是返回類(lèi)型。Func中的字符in、out在實(shí)際代碼中是不會(huì)出現(xiàn)的。
表達(dá)式 Lambda
表達(dá)式位于=>?運(yùn)算符右側(cè)的 Lambda 表達(dá)式稱為“表達(dá)式 lambda”。具體形式:(input-parameters) => expression?,表達(dá)式 lambda 會(huì)返回表達(dá)式的結(jié)果。
具體事例,代碼如下:
public Action SuccessPrompt =() => MessageBox.Show("執(zhí)行成功!","提示", MessageBoxButtons.OK, MessageBoxIcon.Information);//沒(méi)有參數(shù),括號(hào)不能省略public Func Compare = (x, y) => x > y;//判斷x是否大于y
語(yǔ)句 Lambda
語(yǔ)句 Lambda 與表達(dá)式 lambda 表達(dá)式類(lèi)似,只是語(yǔ)句括在大括號(hào)中,具體形式:(input-parameters) => { statement; }?。語(yǔ)句 lambda 的主體可以包含任意數(shù)量的語(yǔ)句;但是,實(shí)際上通常不會(huì)多于兩個(gè)或三個(gè)。
具體事例代碼如下:
publicAction Prompt = prompt =>? ? ? ? {? ? ? ? ? ? MessageBox.Show(prompt,"提示", MessageBoxButtons.OK, MessageBoxIcon.Information);? ? ? ? };//僅當(dāng) Lambda 只有一個(gè)輸入?yún)?shù)時(shí),括號(hào)才是可選的;否則括號(hào)是必需的
異步 Lambda
通過(guò)使用?async?和?await?關(guān)鍵字,你可以輕松創(chuàng)建包含異步處理的 lambda 表達(dá)式和語(yǔ)句。其中async?和await?關(guān)鍵字是在 C# 5 中引入的。
await關(guān)鍵字
await?運(yùn)算符應(yīng)用于異步方法中的任務(wù),在方法的執(zhí)行中插入掛起點(diǎn),直到所等待的任務(wù)完成。任務(wù)表示正在進(jìn)行的工作。
await?僅可用于由?async?關(guān)鍵字修改的異步方法中。使用async?修飾符定義并且通常包含一個(gè)或多個(gè)await?表達(dá)式的這類(lèi)方法稱為異步方法。
async修飾符
使用async?修飾符可將方法、lambda 表達(dá)式或匿名方法指定為異步。如果對(duì)方法或表達(dá)式使用此修飾符,則其稱為異步方法。
使用異步 lambda 添加事件處理程序。若要添加此處理程序,請(qǐng)?jiān)?b>lambda 參數(shù)列表前添加async?修飾符,代碼如下:
publicpartialclassForm1:Form{publicForm1(){? ? ? ? InitializeComponent();? ? ? ? button1.Click +=async(sender, e) =>? ? ? ? {awaitExampleMethodAsync();? ? ? ? ? ? textBox1.Text +="\r\nControl returned to Click event handler.\n";? ? ? ? };? ? }privateasyncTaskExampleMethodAsync(){// The following line simulates a task-returning asynchronous process.awaitTask.Delay(1000);//Task.Delay方法只會(huì)延緩異步方法中后續(xù)部分執(zhí)行時(shí)間,當(dāng)程序執(zhí)行到await表達(dá)時(shí),一方面會(huì)立即返回調(diào)用方法,執(zhí)行調(diào)用方法中的剩余部分,這一部分程序的執(zhí)行不會(huì)延長(zhǎng)。另一方面根據(jù)Delay()方法中的參數(shù),延時(shí)對(duì)異步方法中后續(xù)部分的執(zhí)行。}}
Lambda 表達(dá)式和元組
自 C# 7.0(對(duì)應(yīng).NET Framework4.7?和Visual Studio 2017?)起,C# 語(yǔ)言提供對(duì)元組?的內(nèi)置支持。可以提供一個(gè)元組作為 Lambda 表達(dá)式的參數(shù),同時(shí) Lambda 表達(dá)式也可以返回元組。在某些情況下,C# 編譯器使用類(lèi)型推理來(lái)確定元組組件的類(lèi)型。?可通過(guò)用括號(hào)括住用逗號(hào)分隔的組件列表來(lái)定義元組,通常,元組字段命名為Item1?、Item2?等等。但是,可以使用命名組件定義元組。
事例代碼如下:
publicvoidTest1(){? ? Func<(int,int,int), (int,int,int)> doubleItem = ns => (2* ns.Item1,2* ns.Item2,2* ns.Item3);varitemList = (1,2,3);varresultDItemList = (itemList);//結(jié)果為[1, 2, 9]}publicvoidTest2(){? ? Func<(intx,inty,intz), (int,int,int)> doubleItem = ns => (2* ns.x,2* ns.y,2* ns.z);varitemList = (1,2,3);varresultDItemList = (itemList);//結(jié)果為[1, 2, 9]}
含標(biāo)準(zhǔn)查詢運(yùn)算符的 Lambda
在其他實(shí)現(xiàn)中,LINQ to Objects 有一個(gè)輸入?yún)?shù),其類(lèi)型是泛型委托?Func?系列中的一種。這些委托使用類(lèi)型參數(shù)來(lái)定義輸入?yún)?shù)的數(shù)量和類(lèi)型,以及委托的返回類(lèi)型。Func?委托對(duì)于封裝用戶定義的表達(dá)式非常有用,這些表達(dá)式將應(yīng)用于一組源數(shù)據(jù)中的每個(gè)元素。
標(biāo)準(zhǔn)查詢運(yùn)算符是組成 LINQ 模式的方法。這些方法中的大多數(shù)都作用于序列;其中序列指其類(lèi)型實(shí)現(xiàn)IEnumerable<T>?接口或IQueryable<T>?接口的對(duì)象。標(biāo)準(zhǔn)查詢運(yùn)算符提供包括篩選、投影、聚合、排序等在內(nèi)的查詢功能。
共有兩組 LINQ 標(biāo)準(zhǔn)查詢運(yùn)算符,一組作用于類(lèi)型IEnumerable<T>?的對(duì)象,另一組作用于類(lèi)型IQueryable<T>?的對(duì)象。構(gòu)成每個(gè)集合的方法分別是Enumerable?和Queryable?類(lèi)的靜態(tài)成員。這些方法被定義為作為方法運(yùn)行目標(biāo)的類(lèi)型的擴(kuò)展方法?。這意味著可以使用靜態(tài)方法語(yǔ)法或?qū)嵗椒ㄕZ(yǔ)法來(lái)調(diào)用它們。
具體事例代碼如下:
publicclassSutdent{? ? ? ? ? ? public string Id {get;set; }? ? ? ? ? ? public string Name {get;set; }? ? ? ? ? ? publicintAge {get;set; }? ? ? ? }staticvoidMain(string[] args)? ? ? ? {List studentList =newList();? ? ? ? ? ? studentList.Add(newSutdent {Id ="001", Name ="張三", Age =18});? ? ? ? ? ? studentList.Add(newSutdent {Id ="002", Name ="李四", Age =19});? ? ? ? ? ? studentList.Add(newSutdent {Id ="003", Name ="王五", Age =16});? ? ? ? ? ? studentList.Add(newSutdent {Id ="004", Name ="趙六", Age =17});List list1 = studentList.FindAll(st => st.Age >17);//選擇年齡大于17的所有學(xué)生List list2 = studentList.Where(st => st.Age >17).ToList();//選擇年齡大于17的所有學(xué)生studentList.Sort((st1,st2)=>st2.Age-st1.Age);//按Age降序排列List list3 = studentList.Select(st => st.Name).ToList();//選擇列表中的所有名字}
Lambda 表達(dá)式中的類(lèi)型推理
編寫(xiě) Lambda 時(shí),通常不必為輸入?yún)?shù)指定類(lèi)型,因?yàn)榫幾g器可以根據(jù) Lambda 主體、參數(shù)類(lèi)型以及 C# 語(yǔ)言規(guī)范中描述的其他因素來(lái)推斷類(lèi)型。
lambda 類(lèi)型推理的一般規(guī)則如下:
Lambda 包含的參數(shù)數(shù)量必須與委托類(lèi)型包含的參數(shù)數(shù)量相同。
Lambda 中的每個(gè)輸入?yún)?shù)必須都能夠隱式轉(zhuǎn)換為其對(duì)應(yīng)的委托參數(shù)。
Lambda 的返回值(如果有)必須能夠隱式轉(zhuǎn)換為委托的返回類(lèi)型。
總結(jié)
通過(guò)上邊的講解,我們可以看出Lambda表達(dá)式的用法非常的簡(jiǎn)單,特別在?標(biāo)準(zhǔn)查詢運(yùn)算符中應(yīng)用非常廣泛,提高了編程效率,且寫(xiě)出的代碼非常的簡(jiǎn)潔。文中若有不足之處,還望海涵,博文寫(xiě)作不易希望多多支持,后續(xù)會(huì)更新更多內(nèi)容,感興趣的朋友可以加關(guān)注,歡迎留言交流!
看我主頁(yè)簡(jiǎn)介免費(fèi)C++學(xué)習(xí)資源,視頻教程、職業(yè)規(guī)劃、面試詳解、學(xué)習(xí)路線、開(kāi)發(fā)工具
每晚8點(diǎn)直播講解C++編程技術(shù)。