C#中閉包的實(shí)現(xiàn)和注意事項(xiàng)詳解
閉包的定義
閉包并不是某一個(gè)語言中特有的概念,在主流的編程語言中都有這個(gè)特性。閉包可以讓一個(gè)內(nèi)部方法可以訪問它所在外部方法中的變量,并可以對(duì)變量的值進(jìn)行修改,即使在外部方法的生命周期已經(jīng)結(jié)束后。
不知道在看完上面這一串定義之后,可以明白閉包是個(gè)什么東西嗎,反正我在一開始看完是不知道這是在說什么,不過沒關(guān)系,我們可以先往下具體看閉包的一個(gè)例子。
C#中閉包的實(shí)現(xiàn)
下面是一個(gè)最簡單的閉包,這里使用了委托和Lambda表達(dá)式,不過使用局部函數(shù)也是一樣的效果,這兩者之間的區(qū)別我們之后去討論,這里就以使用委托的版本來看一下后臺(tái)干了什么。
class Program { // 使用委托和 Lambda 表達(dá)式 static void Main(string[] args) { int num = 0; Action test = () => { num++; Console.WriteLine(num); }; test(); } }
class Program { // 使用局部函數(shù) static void Main(string[] args) { int num = 0; Test(); return; void Test() { num++; Console.WriteLine(num); } } }
我們看構(gòu)建后的低級(jí)別C#可以看出來,在Main方法中new了一個(gè)類,這個(gè)類中有一個(gè)num的成員變量,我們形成閉包的方法實(shí)際操作的是這個(gè)成員變量,這就解釋了為什么外部方法的生命周期結(jié)束后,仍然可以訪問到在其定義的變量。
閉包的注意事項(xiàng)
Lambda表達(dá)式和局部方法
我們先看下面這個(gè)場景,在這個(gè)場景我們使用了一個(gè)類中的某個(gè)方法并想要在完成后執(zhí)行一個(gè)回調(diào)。
這里使用Lambda表達(dá)式和使用局部函數(shù)有兩種情況,一是會(huì)捕獲封閉范圍內(nèi)的變量時(shí),如下面這段代碼,這時(shí)使用局部函數(shù)和使用Lambda表達(dá)式一樣,都會(huì)形成一個(gè)閉包,并使用類的成員變量來實(shí)現(xiàn),如我在解釋C#中閉包的實(shí)現(xiàn)時(shí)舉得那個(gè)例子。
class Program { static void Main(string[] args) { int num = 0; TestClass test = new TestClass(); for (int i = 0; i < 3; i++) { test.DoSomething(() => { num++; Console.WriteLine($"DoSomething finish {num}"); }); } } } class TestClass { public void DoSomething(Action callback) { Console.WriteLine("DoSomething"); // 執(zhí)行回調(diào) callback.Invoke(); } }
但是如果沒有捕獲任何變量時(shí),如下面這種情況,可以看到使用局部函數(shù)的方式?jīng)]有形成閉包,局部函數(shù)最終變成了一個(gè)靜態(tài)函數(shù)。而使用委托和 Lambda 表達(dá)式的盡管沒有捕獲任何變量,但還是創(chuàng)建了一個(gè)類,雖然這個(gè)類只會(huì)實(shí)例化一次。
class Program { // 使用局部函數(shù),且沒有捕獲封閉范圍內(nèi)的變量 static void Main(string[] args) { TestClass test = new TestClass(); for (int i = 0; i < 3; i++) { test.DoSomething(DoSomethingCallBack); } return; void DoSomethingCallBack() { Console.WriteLine($"DoSomething finish"); } } }
class Program { // 使用委托和 Lambda 表達(dá)式,且沒有捕獲封閉范圍內(nèi)的變量 static void Main(string[] args) { TestClass test = new TestClass(); for (int i = 0; i < 3; i++) { test.DoSomething(() => { Console.WriteLine("DoSomething finish"); }); } } }
配合for循環(huán)返回多個(gè)函數(shù)
閉包和for循環(huán)這也是一個(gè)經(jīng)典的錯(cuò)誤了,讓我們來看下面這段代碼,這里乍一看沒什么問題,我們預(yù)期輸出的結(jié)果是0,1。
class Program { static void Main(string[] args) { int count = 2; Action[] fun = CreateActions(count); for (int i = 0; i < count; i++) { fun[i].Invoke(); } return; Action[] CreateActions(int count) { Action[] actions = new Action[count]; for (int i = 0; i < count; i++) { actions[i] = () => Console.WriteLine(i); } return actions; } } }
但是我們實(shí)際運(yùn)行一下,可以發(fā)現(xiàn)輸出的結(jié)果是2,2。
這里我們看編譯之后的代碼,可以看出我們本質(zhì)操控的其實(shí)是同一個(gè)<>c__DisplayClass0_0
對(duì)象的成員變量i
,所以自然輸出都都是i
自增之后的值。
這里改起來也比較容易,只需將for循環(huán)改成下面這樣就行了。
for (int i = 0; i < count; i++) { int j = i; actions[i] = () => Console.WriteLine(j); }
這里要注意,int j = i;
一定要在for循環(huán)里面,這樣閉包才會(huì)創(chuàng)建3個(gè)不同的對(duì)象。如果像下面這樣寫,本質(zhì)和最開始沒有太大區(qū)別,還是操縱著一個(gè)對(duì)象。
int j; for (int i = 0; i < count; i++) { j = i; actions[i] = () => Console.WriteLine(j); }
以上就是C#中閉包的實(shí)現(xiàn)和注意事項(xiàng)詳解的詳細(xì)內(nèi)容,更多關(guān)于C#閉包實(shí)現(xiàn)和注意事項(xiàng)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Windows Form 分頁 具體實(shí)現(xiàn)
其實(shí)功能實(shí)現(xiàn)很簡單。我做的是一個(gè)通用的分頁控件。項(xiàng)目時(shí)間很緊,可能有點(diǎn)粗糙。歡迎大家斧正。不說了直接貼代碼吧2013-12-12C#使用linq對(duì)數(shù)組進(jìn)行篩選排序的方法
這篇文章主要介紹了C#使用linq對(duì)數(shù)組進(jìn)行篩選排序的方法,實(shí)例分析了C#實(shí)用linq擴(kuò)展進(jìn)行數(shù)組排序的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04C#遍歷得到checkboxlist選中值和設(shè)置選中項(xiàng)的代碼
這篇文章主要介紹了C#遍歷得到checkboxlist選中值和設(shè)置選中項(xiàng)的代碼,代碼簡單易懂,具有參考借鑒價(jià)值,需要的朋友可以參考下2016-08-08C#線程執(zhí)行超時(shí)處理與并發(fā)線程數(shù)控制實(shí)例
這篇文章主要介紹了C#線程執(zhí)行超時(shí)處理與并發(fā)線程數(shù)控制的方法,實(shí)例講述了并發(fā)執(zhí)行存儲(chǔ)過程的最大個(gè)數(shù),讀者可對(duì)程序稍做改動(dòng)即控制并發(fā)線程數(shù),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-11-11C#中靜態(tài)方法和實(shí)例化方法的區(qū)別、使用
這篇文章主要介紹了C#中靜態(tài)方法和實(shí)例化方法的區(qū)別、使用,文中講解的非常細(xì)致,對(duì)大家的學(xué)習(xí)有所幫助,感興趣的朋友可以了解下2020-06-06C#實(shí)現(xiàn)把圖片轉(zhuǎn)換成二進(jìn)制以及把二進(jìn)制轉(zhuǎn)換成圖片的方法示例
這篇文章主要介紹了C#實(shí)現(xiàn)把圖片轉(zhuǎn)換成二進(jìn)制以及把二進(jìn)制轉(zhuǎn)換成圖片的方法,結(jié)合具體實(shí)例形式分析了基于C#的圖片與二進(jìn)制相互轉(zhuǎn)換以及圖片保存到數(shù)據(jù)庫的相關(guān)操作技巧,需要的朋友可以參考下2017-06-06C#計(jì)算兩個(gè)文件的相對(duì)目錄算法的實(shí)例代碼
現(xiàn)在已知兩個(gè)文件相對(duì)于網(wǎng)站根目錄的路徑,如何計(jì)算相對(duì)路徑呢,有需要的朋友可以參考一下2013-09-09