1. 程式人生 > >C#中的閉包和意想不到的坑

C#中的閉包和意想不到的坑

雖然閉包主要是函數語言程式設計的玩意兒,而C#的最主要特徵是面向物件,但是利用委託或lambda表示式,C#也可以寫出具有函數語言程式設計風味的程式碼。同樣的,使用委託或者lambda表示式,也可以在C#中使用閉包。 > 根據WIKI的定義,閉包又稱語法閉包或函式閉包,是在函數語言程式設計語言中實現語法繫結的一種技術。閉包在實現上是一個結構體,它儲存了一個函式(通常是其入口地址)和一個關聯的環境(相當於一個符號查詢表)。閉包也可以延遲變數的生存週期。 嗯。。看定義好像有點迷糊,讓我們看看下面的例子吧 ```csharp class Program { static Action CreateGreeting(string message) { return () => { Console.WriteLine("Hello " + message); }; } static void Main() { Action action = CreateGreeting("DeathArthas"); action(); } } ``` 這個例子非常簡單,用lambda表示式建立一個Action物件,之後再呼叫這個Action物件。 但是仔細觀察會發現,當Action物件被呼叫的時候,**CreateGreeting**方法已經返回了,作為它的實參的message應該已經被銷燬了,那麼為什麼我們在呼叫Action物件的時候,還是能夠得到正確的結果呢?   原來奧祕就在於,這裡形成了閉包。雖然CreateGreeting已經返回了,但是它的區域性變數被返回的lambda表示式所捕獲,延遲了其生命週期。怎麼樣,這樣再回頭看閉包定義,是不是更清楚了一些?   閉包就是這麼簡單,其實我們經常都在使用,只是有時候我們都不自知而已。比如大家肯定都寫過類似下面的程式碼。 ```csharp void AddControlClickLogger(Control control, string message) { control.Click += delegate { Console.WriteLine("Control clicked: {0}", message); } } ``` 這裡的程式碼其實就用了閉包,因為我們可以肯定,在control被點選的時候,這個message早就超過了它的宣告週期。合理使用閉包,可以確保我們寫出在空間和時間上面解耦的委託。   不過在使用閉包的時候,要注意一個陷阱。因為閉包會延遲區域性變數的生命週期,在某些情況下程式產生的結果會和預想的不一樣。讓我們看看下面的例子。 ```csharp class Program { sta