溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

[C# 基礎(chǔ)知識(shí)系列]專(zhuān)題十一:匿名方法解析

發(fā)布時(shí)間:2020-05-17 10:25:12 來(lái)源:網(wǎng)絡(luò) 閱讀:918 作者:LearningHard 欄目:編程語(yǔ)言

 引言:

  感覺(jué)好久沒(méi)有更新博客了的,真是對(duì)不住大家了。在這個(gè)專(zhuān)題中將介紹匿名方法,匿名方法看名字也能明白,當(dāng)然就是沒(méi)有名字的方法了(現(xiàn)實(shí)生活中也有很多這樣的匿名過(guò)程,如匿名投票,匿名舉報(bào)等等,相信微軟在命名方面肯定是根據(jù)了生活中例子的),然而匿名方法的理解卻不是僅僅是這一句話(這句話指的是沒(méi)有名字的方法),它還有很多內(nèi)容,下面就具體介紹下匿名方法有哪些內(nèi)容 

一、匿名方法

   之前一直認(rèn)為匿名方法是在C# 3.0中提出的,之前之所以這么認(rèn)為主要是因?yàn)橹繡# 3.0中提出了匿名類(lèi)型,所以看到匿名方法就很理所當(dāng)然的認(rèn)為也是在C# 3.0中提出來(lái),然而經(jīng)過(guò)系統(tǒng)的學(xué)習(xí)C#特性后才發(fā)現(xiàn)匿名方法在C# 2.0 的時(shí)候就已經(jīng)提出來(lái)了,從特性的提出發(fā)展中可以看出,微軟的團(tuán)隊(duì)是非常有計(jì)劃的,后面的特性其實(shí)在之前特性的提出就已經(jīng)計(jì)劃好,并且后面的特性都是之前特性演變而來(lái),之所以有新特性的提出,主要是為了方便大家編寫(xiě)程序,減輕程序員的工作,讓編譯器去執(zhí)行更加復(fù)雜的操作,使程序員可以把精力放在實(shí)現(xiàn)自己系統(tǒng)的業(yè)務(wù)邏輯方法(這也是微軟的主要思想,也是大部分軟件所強(qiáng)調(diào)的良好的用戶體驗(yàn)),然而匿名方法也正是建立在C#1.0中委托的基礎(chǔ)上的(同時(shí)C# 2.0中對(duì)委托有所增強(qiáng),提出了泛型委托,以及委托參數(shù)的協(xié)變和逆變,具體的可以參考本系列的前面專(zhuān)題),下面就具體介紹下為什么說(shuō)匿名方法是如何建立在委托基礎(chǔ)之上的(委托是方法的包裝,匿名方法也是方法,只是匿名方法是沒(méi)有名字的方法而已,所以委托也可以包裝匿名方法)。

  首先,先介紹下匿名方法的概念,匿名方法——沒(méi)有名字的方法(方法也就是數(shù)學(xué)中的函數(shù)的概念),匿名方法只是在我們編寫(xiě)的源代碼中沒(méi)有指定名字而已,其實(shí)編譯器會(huì)幫匿名方法生成一個(gè)名字,然而就是因?yàn)樵谠创a中沒(méi)有名字,所以匿名方法只能在定義的時(shí)候才能調(diào)用,在其他地方不能被調(diào)用(匿名方法把方法的定義和方法的實(shí)現(xiàn)內(nèi)嵌在一起),下面通過(guò)一個(gè)例子來(lái)看看匿名方法的使用和如何與委托關(guān)聯(lián)起來(lái)的:

  1. namespace 匿名方法Demo  
  2. {  
  3.     class Program  
  4.     {  
  5.         // 定義投票委托  
  6.         delegate void VoteDelegate(string name);  
  7.  
  8.         static void Main(string[] args)  
  9.         {  
  10.             // 實(shí)例化委托對(duì)象  
  11.             VoteDelegate votedelegate = new VoteDelegate(new Friend().Vote);  
  12.  
  13.             // 使用匿名方法的代碼  
  14.             // 匿名方法內(nèi)聯(lián)了一個(gè)委托實(shí)例(可以對(duì)照上面的委托實(shí)例化的代碼來(lái)理解)  
  15.             // 使用匿名方法后,我們就不需要定義一個(gè)Friend類(lèi)以及單獨(dú)定義一個(gè)投票方法  
  16.             // 這樣就可以減少代碼量,代碼少了,閱讀起來(lái)就容易多了,以至于不會(huì)讓過(guò)多的回調(diào)方法的定義而弄糊涂了  
  17.             //VoteDelegate votedelegate = delegate(string nickname)  
  18.             //{  
  19.             //    Console.WriteLine("昵稱為:{0} 來(lái)幫Learning Hard投票了", nickname);  
  20.             //};  
  21.  
  22.             // 通過(guò)調(diào)用托來(lái)回調(diào)Vote()方法  
  23.             votedelegate("SomeBody");  
  24.             Console.Read();  
  25.         }  
  26.     }  
  27.  
  28.     public class Friend  
  29.     {  
  30.         // 朋友的投票方法  
  31.         public void Vote(string nickname)  
  32.         {  
  33.             Console.WriteLine("昵稱為:{0} 來(lái)幫Learning Hard投票了", nickname);  
  34.         }  
  35.     }  

  因?yàn)榍岸螘r(shí)間參加了51博客大賽,在投票階段也拉了好多朋友來(lái)幫忙投票的,所以為了感謝他們,所以上面就以投票作為例子來(lái)引出匿名方法,注釋的部分中已經(jīng)解釋了匿名方法的好處的,可以幫助我們減少書(shū)寫(xiě)代碼量,便于閱讀,然而上面地方可以使用匿名方法來(lái)代替委托呢?是不是所有使用委托的地方我們都需要用匿名方法去代替的呢?事實(shí)不是這樣的,因?yàn)槟涿椒ㄊ菦](méi)有名字的方法,所以在其他地方就不能被調(diào)用,所以不具有復(fù)用作用,并且匿名方法自動(dòng)形成"閉包"(如果對(duì)于閉包不理解的朋友可以參考這兩個(gè)鏈接:http://baike.baidu.com/view/648413.htm 和http://zh.wikipedia.org/wiki/閉包_(計(jì)算機(jī)科學(xué)) ,我理解的閉包大概是當(dāng)一個(gè)函數(shù)中(外部函數(shù))調(diào)用了另個(gè)一個(gè)函數(shù)(稱內(nèi)部函數(shù))時(shí),當(dāng)內(nèi)部函數(shù)使用了外部函數(shù)中的變量時(shí),這樣就可能會(huì)形成閉包。具體的概念可以參考上面的兩個(gè)鏈接,關(guān)于閉包在后面部分也會(huì)給出相關(guān)的例子來(lái)幫助大家理解,由于匿名函數(shù)會(huì)形成閉包,這就會(huì)延長(zhǎng)變量的生命周期)。所以如果委托包裝的方法相對(duì)簡(jiǎn)單(就像上面代碼中只是單獨(dú)一行輸出語(yǔ)句),并且這個(gè)方法在其他地方使用的頻率很低時(shí),這時(shí)候就可以考慮用匿名方法來(lái)代替委托。

二、使用匿名方法來(lái)忽略委托參數(shù)

  第一部分主要介紹了匿名方法的概念,使用以及介紹了我所理解的為什么會(huì)有匿名方法的提出(為了方便我們實(shí)例化委托實(shí)例,通過(guò)匿名方法可以內(nèi)聯(lián)委托實(shí)例,這樣就避免額外定義一個(gè)實(shí)例方法,減少代碼量,利于閱讀),在這一部分中將介紹匿名方法的另外一個(gè)好處——忽略委托參數(shù)。下面通過(guò)一個(gè)示例代碼來(lái)來(lái)幫助大家理解,代碼中會(huì)有詳細(xì)的注釋?zhuān)赃@里就不多說(shuō)了,直接看代碼了:

  1. namespace 忽略委托參數(shù)Demo  
  2. {  
  3.     class Program  
  4.     {  
  5.         static void Main(string[] args)  
  6.         {  
  7.             // Timer類(lèi)在應(yīng)用程序中生成定期事件  
  8.             System.Timers.Timer timer = new System.Timers.Timer();  
  9.  
  10.             // 該值指示是否引發(fā)Elapsed事件  
  11.             timer.Enabled = true;  
  12.  
  13.             // 設(shè)置引發(fā)Elapsed事件的間隔  
  14.             timer.Interval = 1000;  
  15.  
  16.             // Elapsed事件是達(dá)到間隔時(shí)發(fā)生,前面設(shè)置了時(shí)間間隔為1秒,  
  17.             // 所以每一秒就會(huì)觸發(fā)Elapsed事件,從而回調(diào)timer_Elapsed方法,輸出當(dāng)前的時(shí)間  
  18.             // timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);  
  19.  
  20.             // 此時(shí)timer_Elapsed方法中的參數(shù)根本就不需要,所以我們可以使用匿名方法來(lái)省略委托參數(shù)  
  21.             // 省略了參數(shù)后我們的代碼就更加簡(jiǎn)潔了,看的多舒服啊  
  22.             // 在開(kāi)發(fā)WinForm程序中我們經(jīng)常會(huì)用不到委托的參數(shù),此時(shí)就可以使用匿名方法來(lái)省略參數(shù)  
  23.             timer.Elapsed += delegate 
  24.             {  
  25.                 Console.WriteLine(DateTime.Now);  
  26.             };  
  27.  
  28.             Console.Read();  
  29.         }  
  30.  
  31.         public static void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)  
  32.         {  
  33.             Console.WriteLine(DateTime.Now);  
  34.         }  
  35.     }  

運(yùn)行結(jié)果為:

[C# 基礎(chǔ)知識(shí)系列]專(zhuān)題十一:匿名方法解析

  上面代碼使用了匿名方法來(lái)省略委托參數(shù),然而對(duì)于編譯器而言,它還是會(huì)調(diào)用委托的構(gòu)造函數(shù)來(lái)實(shí)例化委托,所以如果匿名方法能轉(zhuǎn)換為多個(gè)委托類(lèi)型時(shí),此時(shí)如果省略了委托參數(shù),編譯器就不知道把匿名方法轉(zhuǎn)化為哪個(gè)具體的委托類(lèi)型,所以此時(shí)就會(huì)出現(xiàn)編譯時(shí)錯(cuò)誤,此時(shí)就必須人為的指定參數(shù)來(lái)告訴編譯器如何實(shí)例化委托,下面就以創(chuàng)建線程為例子來(lái)幫助大家理解匿名方法省略委托參數(shù)所帶來(lái)的問(wèn)題(因?yàn)榫€程的創(chuàng)建涉及了兩個(gè)委托類(lèi)型: public delegate void ThreadStart() 和public delegaye void ParameterizedThreadStart(objec obj)):

  1. class Program  
  2.     {  
  3.         static void Main(string[] args)  
  4.         {  
  5.             new Thread(delegate()  
  6.                 {  
  7.                     Console.WriteLine("線程一");  
  8.                 });  
  9.  
  10.             new Thread(delegate(object o)  
  11.             {  
  12.                 Console.WriteLine("線程二");  
  13.             });  
  14.  
  15.             new Thread(delegate 
  16.             {  
  17.                 Console.WriteLine("線程三");  
  18.             });  
  19.             Console.Read();  
  20.         }  
  21.     } 

此時(shí)第三個(gè)創(chuàng)建線程的代碼會(huì)出現(xiàn)下面的編譯錯(cuò)誤:

[C# 基礎(chǔ)知識(shí)系列]專(zhuān)題十一:匿名方法解析

三、在匿名方法中捕捉變量

   前面介紹中提到使用匿名方法時(shí)會(huì)形成閉包,閉包指的就是在匿名方法中捕捉了變量,為了更好的理解閉包的概念,首先需要理解兩個(gè)概念 ——外部變量被捕捉的外部變量,下面通過(guò)一個(gè)例子來(lái)解釋這個(gè)兩個(gè)概念:

  1. class Program  
  2.     {  
  3.         // 定義閉包委托  
  4.         delegate void ClosureDelegate();  
  5.  
  6.         static void Main(string[] args)  
  7.         {  
  8.             closureMethod();  
  9.             Console.Read();  
  10.         }  
  11.  
  12.         // 閉包方法  
  13.         private static void closureMethod()  
  14.         {  
  15.             // outVariable和capturedVariable對(duì)于匿名方法而言都是外部變量  
  16.             // 然而outVariable是未捕獲的外部變量,子所以是未捕獲,是因?yàn)槟涿椒ㄖ形匆迷撟兞? 
  17.             string outVariable = "外部變量";  
  18.  
  19.             //  而capturedVariable是被匿名方法捕獲的外部變量  
  20.             string capturedVariable = "捕獲變量";  
  21.             ClosureDelegate closuredelegate = delegate 
  22.             {  
  23.                 // localvariable是匿名方法中局部變量  
  24.                 string localvariable = "匿名方法局部變量";  
  25.  
  26.                 Console.WriteLine(capturedVariable+" "+localvariable);  
  27.             };  
  28.  
  29.             // 調(diào)用委托  
  30.             closuredelegate();  
  31.         }  
  32.     } 

  一個(gè)變量被捕捉后,被匿名方法捕捉到的是真的變量,而不是創(chuàng)建委托實(shí)例時(shí)該變量的值,并且被匿名方法中捕捉到的變量會(huì)延長(zhǎng)生命周期(意思是說(shuō)對(duì)于一個(gè)被捕捉的變量,只要還有任何委托實(shí)例引用它,它就一直存在,而不會(huì)當(dāng)委托實(shí)例調(diào)用結(jié)束后就被垃圾回收),下面通過(guò)一個(gè)具體的例子看看匿名方法是如何延長(zhǎng)變量的生命周期的:

  1. class Program  
  2.     {  
  3.         // 定義閉包委托  
  4.         delegate void ClosureDelegate();  
  5.  
  6.         static void Main(string[] args)  
  7.         {  
  8.             ClosureDelegate test = CreateDelegateInstance();  
  9.             test();  
  10.      
  11.             Console.Read();  
  12.         }  
  13.  
  14.         // 閉包延長(zhǎng)變量的生命周期  
  15.         private static ClosureDelegate CreateDelegateInstance()  
  16.         {  
  17.             int count = 1;  
  18.     
  19.             ClosureDelegate closuredelegate = delegate 
  20.             {  
  21.                 Console.WriteLine(count);  
  22.                 count++;  
  23.             };  
  24.  
  25.             // 調(diào)用委托  
  26.             closuredelegate();  
  27.             return closuredelegate;  
  28.         }  

運(yùn)行結(jié)果為:

[C# 基礎(chǔ)知識(shí)系列]專(zhuān)題十一:匿名方法解析

  第一行中的1是CreateDelegateInstance內(nèi)部調(diào)用委托實(shí)例輸出的結(jié)果,首先大家肯定認(rèn)為count是在棧上分配的(因?yàn)閏ount是值類(lèi)型),當(dāng)CreateDelegateInstance方法調(diào)用完后,count的值也會(huì)被銷(xiāo)毀,當(dāng)執(zhí)行 test()這行代碼時(shí),此時(shí)會(huì)回調(diào)匿名方法來(lái)輸出count的值,因?yàn)閏ount被銷(xiāo)毀,按理應(yīng)該會(huì)出現(xiàn)異常才對(duì)的,然而結(jié)果卻為2,然而結(jié)果并沒(méi)有錯(cuò),根據(jù)結(jié)果去倒推的話,可以得出,第二次調(diào)用委托實(shí)例也還是在使用原來(lái)的那個(gè)count,然而之所以我們認(rèn)為會(huì)有異常拋出,主要原因是因?yàn)槲覀冋J(rèn)為count是分配在棧上的,然而事實(shí)并不是這樣的,count變量并不是分配在棧上的,事實(shí)上,編譯器會(huì)創(chuàng)建一個(gè)額外的類(lèi)來(lái)容納變量(此時(shí)count變量時(shí)分配在堆上的),CreateDelegateInstance方法有該類(lèi)的一個(gè)實(shí)例的引用,所以此時(shí)匿名方法捕捉到的變量count是它的一個(gè)引用,而不是真真的值,同時(shí)匿名方法也延長(zhǎng)了變量count的生命周期,使它感覺(jué)不再像是一個(gè)局部變量,反而像是一個(gè)"全局變量"了(因?yàn)榈诙沃姓{(diào)用的委托實(shí)例使用的是同一個(gè)count)。

匿名方法捕捉到的變量,編譯器會(huì)額外創(chuàng)建一個(gè)類(lèi)來(lái)容納該變量,對(duì)于這點(diǎn),大家可以通過(guò)IL反匯編程序進(jìn)行查看,下面是上面程序中使用反匯編程序得到的截圖:

[C# 基礎(chǔ)知識(shí)系列]專(zhuān)題十一:匿名方法解析

  從上面的截圖中可以看出,在源代碼中根本沒(méi)有<>c_DisplayClass1類(lèi)的定義的,然而這個(gè)類(lèi)真是編譯器為我們創(chuàng)建來(lái)容納捕獲變量count的,并且該類(lèi)中容納了CreateDelegateInstance方法,從上圖的左半部分中間語(yǔ)言代碼可以看出,源代碼中定義的CreateDelegateInstance方法具有該<>c_DisplayClass1的一個(gè)引用,在源代碼中使用到的count變量編譯器認(rèn)為是<>c_DisplayClass1中的一個(gè)字段。

四、小結(jié)

   這個(gè)專(zhuān)題中主要介紹了匿名方法的使用以及匿名方法通過(guò)捕獲變量來(lái)延長(zhǎng)變量的生命周期,希望通過(guò)本專(zhuān)題的介紹大家可以對(duì)匿名方法可以有個(gè)全面的認(rèn)識(shí),并且匿名方法也是Lambda表達(dá)式的基礎(chǔ),Lambda表達(dá)式只是C# 3.0中提出更簡(jiǎn)潔的方式來(lái)實(shí)現(xiàn)匿名方法的。

 

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI