Closure in C#

今年因為jQuery的關係,對Javascript有較深入的研究(終於...),也認識了好用的Closure概念。

動態建立一個函數時把特定變數獨立保存一份,在特定場合裡是很犀利簡潔的解法,因此在進階Javascript程式開發中,Closure出現的機率還蠻高的。那麼,.NET, C#呢? 也可以做到Closure嗎?

在C#世界裡,對應Javascript的var myFunc = function(s) { alert(s); }這類變數結合匿名函數的概念,一般是用delegate加上匿名方法解決。

在C#裡要實踐Closure也很簡單,在匿名方法中直接呼叫外部的變數就可以了。若該變數屬區域性(Local),你會發現匿名方法在建立時會為這些區域變數建立一個複本供自己專用;反之,若變數並非區域性的,則會是多個匿名方法共用。

我試著用以下程式碼來示範。透過迴圈我產生了四個匿名方法放入funcs陣列中,匿名方法裡則大方地呼叫了intVar及outVar,其中intVar的存在範圍只限於for迴圈內,符合區域變數的定義。而在匿名方法中,每呼叫一次就增加一次intVar的值,依前段說明,funcs[0]會有自己的intVar,初始值為0;funcs[1]也有自己的intVar,初始值為1。而outVar宣告於for迴圈之外,funcs[0 – 4]共用一個,一修改數值,四個匿名方法都會被影響。

using System;
 
class Program
{
    delegate void MyFunc(string paramStr);
 
    static void Main(string[] args)
    {
        MyFunc[] funcs = new MyFunc[4];
 
        int outVar = 5;
        for (int i = 0; i < 4; i++)
        {
            int intVar = i;
            funcs[i] = (s) =>
            {
                Console.WriteLine(
                    "outVar = {0} intVar = {1} paramStr = {2}",
                    outVar, intVar, s);
                intVar++;
            };
        }
        funcs[0]("First Call");
        funcs[0]("Second Call");
        funcs[2]("Test1");
        funcs[3]("Test2");
        outVar = 6;
        funcs[2]("Test3");
        funcs[3]("Test4");
        Console.Read();
    }
}

執行結果為:

outVar = 5 intVar = 0 paramStr = First Call
outVar = 5 intVar = 1 paramStr = Second Call
outVar = 5 intVar = 2 paramStr = Test1
outVar = 5 intVar = 3 paramStr = Test2
outVar = 6 intVar = 3 paramStr = Test3
outVar = 6 intVar = 4 paramStr = Test4

我們來試著解釋以上結果。在First Call裡,funcs[0]的intVar還是初始值0,在Console.WriteLine後intVar++,因此在Second Call時,intVar就變成1了。而Test1中,funcs[2]有自己獨立的intVar,不受剛才funcs[0]呼叫的影響,顯示出初始值2,Test2裡funcs[3]則顯示intVar初始值3。到目前為止所有funcs[*]的outVar都是5。接著我們改變outVar=6,在Test3, Test4裡再次呼叫funcs[2], funcs[3],一如預期分別得到遞增後的intVar--3與4,而outVar則顯示新值6。

很有趣吧! 這是怎麼做到的? 其實Compiler在背後做了一大堆工作,大顆汗小顆汗才實踐了這番效果。大致原理是Compiler自行宣告了一個內部使用的Class(假設叫ToToMoMoClass),並宣告了一個intVar作為Class Memeber,接著為每個匿名方法建立一個ToToMoMoClass Instance,由於有4個Instance,就實踐了每個匿名方法都有自己一份intVar的效果。以上只是概念化的解釋,實踐做法裡還有不少深奧細節,有興趣深入的話可以參考延伸閱讀中的文章,有較詳盡的說明。

【延伸閱讀】

歡迎推文分享:
Published 26 December 2009 12:01 PM 由 Jeffrey
Filed under:
Views: 12,794



意見

沒有意見

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<December 2009>
SunMonTueWedThuFriSat
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789
 
RSS
創用 CC 授權條款
【廣告】
twMVC

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication