C#学习基本概念之匿名方法及Lambda表达式
在 2.0 之前的 C# 版本中,聲明委托的唯一方法是使用命名方法。 ?C# 2.0 引入了匿名方法,而在 C# 3.0 及更高版本中,Lambda 表達(dá)式取代了匿名方法,作為編寫(xiě)內(nèi)聯(lián)代碼的首選方式。 ? 不過(guò),本主題中有關(guān)匿名方法的信息同樣也適用于 Lambda 表達(dá)式。 ?有一種情況下,匿名方法提供了 Lambda 表達(dá)式中所沒(méi)有的功能。 ? 您可使用匿名方法來(lái)忽略參數(shù)列表。 ?這意味著匿名方法可轉(zhuǎn)換為具有各種簽名的委托。 ?這對(duì)于 Lambda 表達(dá)式來(lái)說(shuō)是不可能的。 ?有關(guān) lambda 表達(dá)式的更多特定信息,請(qǐng)參見(jiàn) Lambda 表達(dá)式。
要將代碼塊傳遞為委托參數(shù),創(chuàng)建匿名方法則是唯一的方法。 ?這里是兩個(gè)示例:
button1.Click?+=?delegate(System.Object?o,?System.EventArgs?e) {?System.Windows.Forms.MessageBox.Show("Click!");?}; //?Create?a?delegate.delegate?void?Del(int?x);????//?Instantiate?the?delegate?using?an?anonymous?method.Del?d?=?delegate(int?k)?{?/*?...?*/?};通過(guò)使用匿名方法,由于您不必創(chuàng)建單獨(dú)的方法,因此減少了實(shí)例化委托所需的編碼系統(tǒng)開(kāi)銷(xiāo)。
例如,如果創(chuàng)建方法所需的系統(tǒng)開(kāi)銷(xiāo)是不必要的,則指定代碼塊(而不是委托)可能非常有用。 ?啟動(dòng)新線(xiàn)程即是一個(gè)很好的示例。 ?無(wú)需為委托創(chuàng)建更多方法,線(xiàn)程類(lèi)即可創(chuàng)建一個(gè)線(xiàn)程并且包含該線(xiàn)程執(zhí)行的代碼。
void?StartThread(){System.Threading.Thread?t1?=?new?System.Threading.Thread(delegate(){System.Console.Write("Hello,?");System.Console.WriteLine("World!");});t1.Start();}備注
匿名方法的參數(shù)的范圍是“匿名方法塊”。
如果目標(biāo)在塊外部,那么,在匿名方法塊內(nèi)使用跳轉(zhuǎn)語(yǔ)句(如 goto、break 或 continue)是錯(cuò)誤的。 ?如果目標(biāo)在塊內(nèi)部,在匿名方法塊外部使用跳轉(zhuǎn)語(yǔ)句(如 goto、break 或 continue)也是錯(cuò)誤的。
如果局部變量和參數(shù)的范圍包含匿名方法聲明,則該局部變量和參數(shù)稱(chēng)為該匿名方法的“外部”變量。 ?例如,下面代碼段中的 n 即是一個(gè)外部變量:
?int?n?=?0;Del?d?=?delegate()?{?System.Console.WriteLine("Copy?#:{0}",?++n);?};外部變量的引用n被認(rèn)為是捕獲在創(chuàng)建委托時(shí)。 ?與本地變量不同,捕獲的變量的生存期內(nèi)擴(kuò)展,直到引用該匿名方法委托被垃圾回收。
匿名方法不能訪(fǎng)問(wèn)外部范圍的 ref 或 out 參數(shù)。
在“匿名方法塊”中不能訪(fǎng)問(wèn)任何不安全代碼。
在 is 運(yùn)算符的左側(cè)不允許使用匿名方法。
示例
下面的示例演示實(shí)例化委托的兩種方法:
- 使委托與匿名方法關(guān)聯(lián)。 
- 使委托與命名方法 (DoWork) 關(guān)聯(lián)。 
兩種方法都會(huì)在調(diào)用委托時(shí)顯示一條消息。
?//?Declare?a?delegate.delegate?void?Printer(string?s);????class?TestClass{????????static?void?Main(){????????????//?Instantiate?the?delegate?type?using?an?anonymous?method.Printer?p?=?delegate(string?j){System.Console.WriteLine(j);};????????????//?Results?from?the?anonymous?delegate?call.p("The?delegate?using?the?anonymous?method?is?called.");????????????//?The?delegate?instantiation?using?a?named?method?"DoWork".p?=?new?Printer(TestClass.DoWork);????????????//?Results?from?the?old?style?delegate?call.p("The?delegate?using?the?named?method?is?called.");}????????//?The?method?associated?with?the?named?delegate.static?void?DoWork(string?k){System.Console.WriteLine(k);}}????/*?Output:The?delegate?using?the?anonymous?method?is?called.The?delegate?using?the?named?method?is?called.*/備注:摘自https://msdn.microsoft.com/zh-cn/library/0yw3tz5k.aspx
Lambda:
若要了解有關(guān) Visual Studio 2017 RC 的最新文檔,請(qǐng)參閱 Visual Studio 2017 RC 文檔。
Lambda 表達(dá)式是一種可用于創(chuàng)建委托或表達(dá)式目錄樹(shù)類(lèi)型的匿名函數(shù)。 通過(guò)使用 lambda 表達(dá)式,可以寫(xiě)入可作為參數(shù)傳遞或作為函數(shù)調(diào)用值返回的本地函數(shù)。 Lambda 表達(dá)式對(duì)于編寫(xiě) LINQ 查詢(xún)表達(dá)式特別有用。
若要?jiǎng)?chuàng)建 Lambda 表達(dá)式,需要在 Lambda 運(yùn)算符 => 左側(cè)指定輸入?yún)?shù)(如果有),然后在另一側(cè)輸入表達(dá)式或語(yǔ)句塊。 例如,lambda 表達(dá)式 x => x * x 指定名為 x 的參數(shù)并返回 x 的平方值。 如下面的示例所示,你可以將此表達(dá)式分配給委托類(lèi)型:
delegate?int?del(int?i);?? static?void?Main(string[]?args)?? {??del?myDelegate?=?x?=>?x?*?x;??int?j?=?myDelegate(5);?//j?=?25??}若要?jiǎng)?chuàng)建表達(dá)式目錄樹(shù)類(lèi)型:
using?System.Linq.Expressions;??namespace?ConsoleApplication1?? {??class?Program??{??static?void?Main(string[]?args)??{??Expression<del>?myET?=?x?=>?x?*?x;??}??}?? }=> 運(yùn)算符具有與賦值運(yùn)算符 (=) 相同的優(yōu)先級(jí)并且是右結(jié)合運(yùn)算(參見(jiàn)“運(yùn)算符”文章的“結(jié)合性”部分)。
Lambda 在基于方法的 LINQ 查詢(xún)中用作標(biāo)準(zhǔn)查詢(xún)運(yùn)算符方法(如 Where<TSource>)的參數(shù)。
使用基于方法的語(yǔ)法在 Where<TSource> 類(lèi)中調(diào)用 Enumerable 方法時(shí)(如在 LINQ to Objects 和 LINQ to XML 中一樣),參數(shù)是委托類(lèi)型 System.Func<T,TResult>。 使用 Lambda 表達(dá)式創(chuàng)建該委托最為方便。 例如,當(dāng)你在 System.Linq.Queryable 類(lèi)中調(diào)用相同的方法時(shí)(如在 LINQ to SQL 中一樣),參數(shù)類(lèi)型為 System.Linq.Expressions.Expression<Func>,其中 Func 是最多具有十六個(gè)輸入?yún)?shù)的任何一個(gè) Func 委托。 同樣,Lambda 表達(dá)式只是一種非常簡(jiǎn)潔的構(gòu)造該表達(dá)式目錄樹(shù)的方式。 盡管事實(shí)上通過(guò) Lambda 創(chuàng)建的對(duì)象具有不同的類(lèi)型,但 Lambda 使得 Where 調(diào)用看起來(lái)類(lèi)似。
在上一個(gè)示例中,請(qǐng)注意委托簽名具有一個(gè) int 類(lèi)型的隱式類(lèi)型輸入?yún)?shù),并返回 int。 可以將 Lambda 表達(dá)式轉(zhuǎn)換為該類(lèi)型的委托,因?yàn)樵摫磉_(dá)式也具有一個(gè)輸入?yún)?shù) (x),以及一個(gè)編譯器可隱式轉(zhuǎn)換為 int 類(lèi)型的返回值。 (以下幾節(jié)中將對(duì)類(lèi)型推理進(jìn)行詳細(xì)討論。) 使用輸入?yún)?shù) 5 調(diào)用委托時(shí),它將返回結(jié)果 25。
在 is 或 as 運(yùn)算符的左側(cè)不允許使用 Lambda。
適用于匿名方法的所有限制也適用于 Lambda 表達(dá)式。 有關(guān)更多信息,請(qǐng)參見(jiàn)匿名方法。
表達(dá)式 lambda
表達(dá)式位于 => 運(yùn)算符右側(cè)的 lambda 表達(dá)式稱(chēng)為“表達(dá)式 lambda”。 表達(dá)式 lambda 廣泛用于表達(dá)式樹(shù)的構(gòu)造。 表達(dá)式 lambda 會(huì)返回表達(dá)式的結(jié)果,并采用以下基本形式:
(input?parameters)?=>?expression僅當(dāng) lambda 只有一個(gè)輸入?yún)?shù)時(shí),括號(hào)才是可選的;否則括號(hào)是必需的。 括號(hào)內(nèi)的兩個(gè)或更多輸入?yún)?shù)使用逗號(hào)加以分隔:
(x,?y)?=>?x?==?y有時(shí),編譯器難以或無(wú)法推斷輸入類(lèi)型。 如果出現(xiàn)這種情況,你可以按以下示例中所示方式顯式指定類(lèi)型:
(int?x,?string?s)?=>?s.Length?>?x使用空括號(hào)指定零個(gè)輸入?yún)?shù):
()?=>?SomeMethod()在上一個(gè)示例中,請(qǐng)注意表達(dá)式 Lambda 的主體可以包含一個(gè)方法調(diào)用。 但是,如果要?jiǎng)?chuàng)建在 .NET Framework 之外計(jì)算的表達(dá)式目錄樹(shù)(例如,在 SQL Server 中),則不應(yīng)在 lambda 表達(dá)式中使用方法調(diào)用。 在 .NET 公共語(yǔ)言運(yùn)行時(shí)上下文之外,方法將沒(méi)有任何意義。
語(yǔ)句 lambda 與表達(dá)式 lambda 表達(dá)式類(lèi)似,只是語(yǔ)句括在大括號(hào)中:
(input?parameters)?=>?{statement;}語(yǔ)句 lambda 的主體可以包含任意數(shù)量的語(yǔ)句;但是,實(shí)際上通常不會(huì)多于兩個(gè)或三個(gè)。
delegate?void?TestDelegate(string?s);?? …?? TestDelegate?myDel?=?n?=>?{?string?s?=?n?+?"?"?+?"World";?Console.WriteLine(s);?};?? myDel("Hello");像匿名方法一樣,語(yǔ)句 lambda 也不能用于創(chuàng)建表達(dá)式目錄樹(shù)。
異步 lambda
通過(guò)使用 async 和 await 關(guān)鍵字,你可以輕松創(chuàng)建包含異步處理的 lambda 表達(dá)式和語(yǔ)句。 例如,下面的 Windows 窗體示例包含一個(gè)調(diào)用和等待異步方法 ExampleMethodAsync 的事件處理程序。
public?partial?class?Form1?:?Form?? {??public?Form1()??{??InitializeComponent();??}??private?async?void?button1_Click(object?sender,?EventArgs?e)??{??//?ExampleMethodAsync?returns?a?Task.??await?ExampleMethodAsync();??textBox1.Text?+=?"\r\nControl?returned?to?Click?event?handler.\r\n";??}??async?Task?ExampleMethodAsync()??{??//?The?following?line?simulates?a?task-returning?asynchronous?process.??await?Task.Delay(1000);??}?? }你可以使用異步 lambda 添加同一事件處理程序。 若要添加此處理程序,請(qǐng)?jiān)?lambda 參數(shù)列表前添加一個(gè) async 修飾符,如下例所示。
public?partial?class?Form1?:?Form?? {??public?Form1()??{??InitializeComponent();??button1.Click?+=?async?(sender,?e)?=>??{??//?ExampleMethodAsync?returns?a?Task.??await?ExampleMethodAsync();??textBox1.Text?+=?"\r\nControl?returned?to?Click?event?handler.\r\n";??};??}??async?Task?ExampleMethodAsync()??{??//?The?following?line?simulates?a?task-returning?asynchronous?process.??await?Task.Delay(1000);??}?? }有關(guān)如何創(chuàng)建和使用異步方法的詳細(xì)信息,請(qǐng)參閱使用 Async 和 Await 的異步編程。
帶有標(biāo)準(zhǔn)查詢(xún)運(yùn)算符的 lambda
許多標(biāo)準(zhǔn)查詢(xún)運(yùn)算符都具有輸入?yún)?shù),其類(lèi)型是泛型委托系列 Func<T,TResult> 中的一種。 這些委托使用類(lèi)型參數(shù)來(lái)定義輸入?yún)?shù)的數(shù)量和類(lèi)型,以及委托的返回類(lèi)型。Func 委托對(duì)于封裝用戶(hù)定義的表達(dá)式非常有用,這些表達(dá)式將應(yīng)用于一組源數(shù)據(jù)中的每個(gè)元素。 例如,請(qǐng)考慮以下委托類(lèi)型:
public?delegate?TResult?Func<TArg0,?TResult>(TArg0?arg0)可以將委托實(shí)例化為 Func<int,bool> myFunc,其中 int 是輸入?yún)?shù),bool 是返回值。 返回值始終在最后一個(gè)類(lèi)型參數(shù)中指定。Func<int, string, bool> 定義包含兩個(gè)輸入?yún)?shù)(int 和 string)且返回類(lèi)型為 bool 的委托。 當(dāng)調(diào)用下面的 Func 委托時(shí),該委托將返回 true 或 false 以指示輸入?yún)?shù)是否等于 5:
Func<int,?bool>?myFunc?=?x?=>?x?==?5;?? bool?result?=?myFunc(4);?//?returns?false?of?course當(dāng)參數(shù)類(lèi)型為 Expression<Func> 時(shí),你也可以提供 Lambda 表達(dá)式,例如在 System.Linq.Queryable 內(nèi)定義的標(biāo)準(zhǔn)查詢(xún)運(yùn)算符中。 如果指定 Expression<Func> 參數(shù),lambda 將編譯為表達(dá)式目錄樹(shù)。
此處顯示了一個(gè)標(biāo)準(zhǔn)查詢(xún)運(yùn)算符,Count<TSource> 方法:
int[]?numbers?=?{?5,?4,?1,?3,?9,?8,?6,?7,?2,?0?};?? int?oddNumbers?=?numbers.Count(n?=>?n?%?2?==?1);編譯器可以推斷輸入?yún)?shù)的類(lèi)型,或者你也可以顯式指定該類(lèi)型。 這個(gè)特殊 lambda 表達(dá)式將計(jì)算那些除以 2 時(shí)余數(shù)為 1 的整數(shù)的數(shù)量 (n)。
下面一行代碼將生成一個(gè)序列,其中包含 numbers 數(shù)組中在 9 左側(cè)的所有元素,因?yàn)樗切蛄兄械谝粋€(gè)不滿(mǎn)足條件的數(shù)字:
var?firstNumbersLessThan6?=?numbers.TakeWhile(n?=>?n?<?6);此示例展示了如何通過(guò)將輸入?yún)?shù)括在括號(hào)中來(lái)指定多個(gè)輸入?yún)?shù)。 該方法將返回?cái)?shù)字?jǐn)?shù)組中的所有元素,直至遇到一個(gè)值小于其位置的數(shù)字為止。 不要將 lambda 運(yùn)算符 (=>) 與大于等于運(yùn)算符 (>=) 混淆。
var?firstSmallNumbers?=?numbers.TakeWhile((n,?index)?=>?n?>=?index);Lambda 中的類(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)型。 對(duì)于大多數(shù)標(biāo)準(zhǔn)查詢(xún)運(yùn)算符,第一個(gè)輸入是源序列中的元素類(lèi)型。 因此,如果要查詢(xún) IEnumerable<Customer>,則輸入變量將被推斷為 Customer 對(duì)象,這意味著你可以訪(fǎng)問(wèn)其方法和屬性:
customers.Where(c?=>?c.City?==?"London");Lambda 的一般規(guī)則如下:
- Lambda 包含的參數(shù)數(shù)量必須與委托類(lèi)型包含的參數(shù)數(shù)量相同。 
- Lambda 中的每個(gè)輸入?yún)?shù)必須都能夠隱式轉(zhuǎn)換為其對(duì)應(yīng)的委托參數(shù)。 
- Lambda 的返回值(如果有)必須能夠隱式轉(zhuǎn)換為委托的返回類(lèi)型。 
請(qǐng)注意,lambda 表達(dá)式本身沒(méi)有類(lèi)型,因?yàn)槌R?guī)類(lèi)型系統(tǒng)沒(méi)有“Lambda 表達(dá)式”這一內(nèi)部概念。 但是,有時(shí)以一種非正式的方式談?wù)?lambda 表達(dá)式的“類(lèi)型”會(huì)很方便。 在這些情況下,類(lèi)型是指委托類(lèi)型或 lambda 表達(dá)式所轉(zhuǎn)換到的 Expression 類(lèi)型。
Lambda 表達(dá)式中的變量范圍
在定義 lambda 函數(shù)的方法內(nèi)或包含 lambda 表達(dá)式的類(lèi)型內(nèi),Lambda 可以引用范圍內(nèi)的外部變量(請(qǐng)參閱匿名方法)。 以這種方式捕獲的變量將進(jìn)行存儲(chǔ)以備在 lambda 表達(dá)式中使用,即使在其他情況下,這些變量將超出范圍并進(jìn)行垃圾回收。 必須明確地分配外部變量,然后才能在 lambda 表達(dá)式中使用該變量。 下面的示例演示這些規(guī)則:
delegate?bool?D();?? delegate?bool?D2(int?i);??class?Test?? {??D?del;??D2?del2;??public?void?TestMethod(int?input)??{??int?j?=?0;??//?Initialize?the?delegates?with?lambda?expressions.??//?Note?access?to?2?outer?variables.??//?del?will?be?invoked?within?this?method.??del?=?()?=>?{?j?=?10;??return?j?>?input;?};??//?del2?will?be?invoked?after?TestMethod?goes?out?of?scope.??del2?=?(x)?=>?{return?x?==?j;?};??//?Demonstrate?value?of?j:??//?Output:?j?=?0???//?The?delegate?has?not?been?invoked?yet.??Console.WriteLine("j?=?{0}",?j);????????//?Invoke?the?delegate.??bool?boolResult?=?del();??//?Output:?j?=?10?b?=?True??Console.WriteLine("j?=?{0}.?b?=?{1}",?j,?boolResult);??}??static?void?Main()??{??Test?test?=?new?Test();??test.TestMethod(5);??//?Prove?that?del2?still?has?a?copy?of??//?local?variable?j?from?TestMethod.??bool?result?=?test.del2(10);??//?Output:?True??Console.WriteLine(result);??Console.ReadKey();??}?? }下列規(guī)則適用于 lambda 表達(dá)式中的變量范圍:
- 捕獲的變量將不會(huì)被作為垃圾回收,直至引用變量的委托符合垃圾回收的條件。 
- 在外部方法中看不到 lambda 表達(dá)式內(nèi)引入的變量。 
- Lambda 表達(dá)式無(wú)法從封閉方法中直接捕獲 ref 或 out 參數(shù)。 
- Lambda 表達(dá)式中的返回語(yǔ)句不會(huì)導(dǎo)致封閉方法返回。 
- 如果跳轉(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ò)誤的。 
轉(zhuǎn)載于:https://blog.51cto.com/jiaojusuimu/1877137
總結(jié)
以上是生活随笔為你收集整理的C#学习基本概念之匿名方法及Lambda表达式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: 跨域调用报表展现页面的flash打印方法
- 下一篇: 机器学习-损失函数 (转)
