C#中閉包的實(shí)現(xiàn)和注意事項(xiàng)詳解
閉包的定義
閉包并不是某一個(gè)語(yǔ)言中特有的概念,在主流的編程語(yǔ)言中都有這個(gè)特性。閉包可以讓一個(gè)內(nèi)部方法可以訪問(wèn)它所在外部方法中的變量,并可以對(duì)變量的值進(jìn)行修改,即使在外部方法的生命周期已經(jīng)結(jié)束后。
不知道在看完上面這一串定義之后,可以明白閉包是個(gè)什么東西嗎,反正我在一開(kāi)始看完是不知道這是在說(shuō)什么,不過(guò)沒(méi)關(guān)系,我們可以先往下具體看閉包的一個(gè)例子。
C#中閉包的實(shí)現(xiàn)
下面是一個(gè)最簡(jiǎn)單的閉包,這里使用了委托和Lambda表達(dá)式,不過(guò)使用局部函數(shù)也是一樣的效果,這兩者之間的區(qū)別我們之后去討論,這里就以使用委托的版本來(lái)看一下后臺(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#可以看出來(lái),在Main方法中new了一個(gè)類(lèi),這個(gè)類(lèi)中有一個(gè)num的成員變量,我們形成閉包的方法實(shí)際操作的是這個(gè)成員變量,這就解釋了為什么外部方法的生命周期結(jié)束后,仍然可以訪問(wèn)到在其定義的變量。

閉包的注意事項(xiàng)
Lambda表達(dá)式和局部方法
我們先看下面這個(gè)場(chǎng)景,在這個(gè)場(chǎng)景我們使用了一個(gè)類(lèi)中的某個(gè)方法并想要在完成后執(zhí)行一個(gè)回調(diào)。
這里使用Lambda表達(dá)式和使用局部函數(shù)有兩種情況,一是會(huì)捕獲封閉范圍內(nèi)的變量時(shí),如下面這段代碼,這時(shí)使用局部函數(shù)和使用Lambda表達(dá)式一樣,都會(huì)形成一個(gè)閉包,并使用類(lèi)的成員變量來(lái)實(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();
}
}
但是如果沒(méi)有捕獲任何變量時(shí),如下面這種情況,可以看到使用局部函數(shù)的方式?jīng)]有形成閉包,局部函數(shù)最終變成了一個(gè)靜態(tài)函數(shù)。而使用委托和 Lambda 表達(dá)式的盡管沒(méi)有捕獲任何變量,但還是創(chuàng)建了一個(gè)類(lèi),雖然這個(gè)類(lèi)只會(huì)實(shí)例化一次。
class Program
{
// 使用局部函數(shù),且沒(méi)有捕獲封閉范圍內(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á)式,且沒(méi)有捕獲封閉范圍內(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ò)誤了,讓我們來(lái)看下面這段代碼,這里乍一看沒(méi)什么問(wèn)題,我們預(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自增之后的值。

這里改起來(lá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ì)象。如果像下面這樣寫(xiě),本質(zhì)和最開(kāi)始沒(méi)有太大區(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)文章
C#中正則表達(dá)式(Regex)過(guò)濾內(nèi)容的基本使用方法
在 Regex 類(lèi)中提供了很多方法來(lái)操作正則表達(dá)式,這篇文章主要給大家介紹了關(guān)于C#中正則表達(dá)式(Regex)過(guò)濾內(nèi)容的基本使用方法,需要的朋友可以參考下2022-08-08
C#實(shí)現(xiàn)從網(wǎng)絡(luò)同步標(biāo)準(zhǔn)北京時(shí)間的方法
這篇文章主要介紹了C#實(shí)現(xiàn)從網(wǎng)絡(luò)同步標(biāo)準(zhǔn)北京時(shí)間的方法,涉及C#操作時(shí)間的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03
WinForm實(shí)現(xiàn)為ComboBox綁定數(shù)據(jù)源并提供下拉提示功能
這篇文章主要介紹了WinForm實(shí)現(xiàn)為ComboBox綁定數(shù)據(jù)源并提供下拉提示功能,是非常實(shí)用的功能,需要的朋友可以參考下2014-08-08
詳解C#如何為某個(gè)方法設(shè)定執(zhí)行超時(shí)時(shí)間
這篇文章主要為大家詳細(xì)介紹一下C#如何為某個(gè)方法設(shè)定執(zhí)行超時(shí)時(shí)間,文中的示例代碼簡(jiǎn)潔易懂,具有一定的借鑒價(jià)值,有需要的小伙伴可以學(xué)習(xí)一下2023-10-10
C#從windows剪貼板獲取并顯示文本內(nèi)容的方法
這篇文章主要介紹了C#從windows剪貼板獲取并顯示文本內(nèi)容的方法,涉及C#操作剪貼板的相關(guān)技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04

