您好,登錄后才能下訂單哦!
引言:
感覺(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)的:
- namespace 匿名方法Demo
- {
- class Program
- {
- // 定義投票委托
- delegate void VoteDelegate(string name);
- static void Main(string[] args)
- {
- // 實(shí)例化委托對(duì)象
- VoteDelegate votedelegate = new VoteDelegate(new Friend().Vote);
- // 使用匿名方法的代碼
- // 匿名方法內(nèi)聯(lián)了一個(gè)委托實(shí)例(可以對(duì)照上面的委托實(shí)例化的代碼來(lái)理解)
- // 使用匿名方法后,我們就不需要定義一個(gè)Friend類(lèi)以及單獨(dú)定義一個(gè)投票方法
- // 這樣就可以減少代碼量,代碼少了,閱讀起來(lái)就容易多了,以至于不會(huì)讓過(guò)多的回調(diào)方法的定義而弄糊涂了
- //VoteDelegate votedelegate = delegate(string nickname)
- //{
- // Console.WriteLine("昵稱為:{0} 來(lái)幫Learning Hard投票了", nickname);
- //};
- // 通過(guò)調(diào)用托來(lái)回調(diào)Vote()方法
- votedelegate("SomeBody");
- Console.Read();
- }
- }
- public class Friend
- {
- // 朋友的投票方法
- public void Vote(string nickname)
- {
- Console.WriteLine("昵稱為:{0} 來(lái)幫Learning Hard投票了", nickname);
- }
- }
- }
因?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ō)了,直接看代碼了:
- namespace 忽略委托參數(shù)Demo
- {
- class Program
- {
- static void Main(string[] args)
- {
- // Timer類(lèi)在應(yīng)用程序中生成定期事件
- System.Timers.Timer timer = new System.Timers.Timer();
- // 該值指示是否引發(fā)Elapsed事件
- timer.Enabled = true;
- // 設(shè)置引發(fā)Elapsed事件的間隔
- timer.Interval = 1000;
- // Elapsed事件是達(dá)到間隔時(shí)發(fā)生,前面設(shè)置了時(shí)間間隔為1秒,
- // 所以每一秒就會(huì)觸發(fā)Elapsed事件,從而回調(diào)timer_Elapsed方法,輸出當(dāng)前的時(shí)間
- // timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
- // 此時(shí)timer_Elapsed方法中的參數(shù)根本就不需要,所以我們可以使用匿名方法來(lái)省略委托參數(shù)
- // 省略了參數(shù)后我們的代碼就更加簡(jiǎn)潔了,看的多舒服啊
- // 在開(kāi)發(fā)WinForm程序中我們經(jīng)常會(huì)用不到委托的參數(shù),此時(shí)就可以使用匿名方法來(lái)省略參數(shù)
- timer.Elapsed += delegate
- {
- Console.WriteLine(DateTime.Now);
- };
- Console.Read();
- }
- public static void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
- {
- Console.WriteLine(DateTime.Now);
- }
- }
- }
運(yùn)行結(jié)果為:
上面代碼使用了匿名方法來(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)):
- class Program
- {
- static void Main(string[] args)
- {
- new Thread(delegate()
- {
- Console.WriteLine("線程一");
- });
- new Thread(delegate(object o)
- {
- Console.WriteLine("線程二");
- });
- new Thread(delegate
- {
- Console.WriteLine("線程三");
- });
- Console.Read();
- }
- }
此時(shí)第三個(gè)創(chuàng)建線程的代碼會(huì)出現(xiàn)下面的編譯錯(cuò)誤:
三、在匿名方法中捕捉變量
前面介紹中提到使用匿名方法時(shí)會(huì)形成閉包,閉包指的就是在匿名方法中捕捉了變量,為了更好的理解閉包的概念,首先需要理解兩個(gè)概念 ——外部變量和被捕捉的外部變量,下面通過(guò)一個(gè)例子來(lái)解釋這個(gè)兩個(gè)概念:
- class Program
- {
- // 定義閉包委托
- delegate void ClosureDelegate();
- static void Main(string[] args)
- {
- closureMethod();
- Console.Read();
- }
- // 閉包方法
- private static void closureMethod()
- {
- // outVariable和capturedVariable對(duì)于匿名方法而言都是外部變量
- // 然而outVariable是未捕獲的外部變量,子所以是未捕獲,是因?yàn)槟涿椒ㄖ形匆迷撟兞?
- string outVariable = "外部變量";
- // 而capturedVariable是被匿名方法捕獲的外部變量
- string capturedVariable = "捕獲變量";
- ClosureDelegate closuredelegate = delegate
- {
- // localvariable是匿名方法中局部變量
- string localvariable = "匿名方法局部變量";
- Console.WriteLine(capturedVariable+" "+localvariable);
- };
- // 調(diào)用委托
- closuredelegate();
- }
- }
一個(gè)變量被捕捉后,被匿名方法捕捉到的是真的變量,而不是創(chuàng)建委托實(shí)例時(shí)該變量的值,并且被匿名方法中捕捉到的變量會(huì)延長(zhǎng)生命周期(意思是說(shuō)對(duì)于一個(gè)被捕捉的變量,只要還有任何委托實(shí)例引用它,它就一直存在,而不會(huì)當(dāng)委托實(shí)例調(diào)用結(jié)束后就被垃圾回收),下面通過(guò)一個(gè)具體的例子看看匿名方法是如何延長(zhǎng)變量的生命周期的:
- class Program
- {
- // 定義閉包委托
- delegate void ClosureDelegate();
- static void Main(string[] args)
- {
- ClosureDelegate test = CreateDelegateInstance();
- test();
- Console.Read();
- }
- // 閉包延長(zhǎng)變量的生命周期
- private static ClosureDelegate CreateDelegateInstance()
- {
- int count = 1;
- ClosureDelegate closuredelegate = delegate
- {
- Console.WriteLine(count);
- count++;
- };
- // 調(diào)用委托
- closuredelegate();
- return closuredelegate;
- }
- }
運(yùn)行結(jié)果為:
第一行中的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)行查看,下面是上面程序中使用反匯編程序得到的截圖:
從上面的截圖中可以看出,在源代碼中根本沒(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)匿名方法的。
免責(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)容。