委託、Lambda表示式、事件系列05,Action委託與閉包

Darren Ji發表於2014-10-02

來看使用Action委託的一個例項:

        static void Main(string[] args)
        {
            int i = 0;
            Action a = () => i++;
            a();
            a();
            Console.WriteLine(i);
        }

12

結果是期望能的2。但令人好奇的是:棧上的變數i是如何傳遞給Action委託的?

 

反編譯進行檢視,首先看Main方法對應的IL程式碼:

13

 

再看c_DisplayClass1的IL程式碼:

14

從中可以看出:
→在託管堆上建立了一個名為c_DisplayClass1的例項
→把棧上變數i的值賦值給了c_DisplayClass1的例項欄位i
→編譯器() => i++;Lambda表示式表示的匿名委託起了個<Main>b_0的方法名,併成為了c_DisplayClass1的例項方法
→把c_DisplayClass1的例項方法<Main>b_0賦值給Action委託變數
→最後呼叫委託2次,這2次都是針對c_DisplayClass1的例項欄位i

 

換句話說,在託管堆上建立了物件例項,形成"閉包"。棧上的變數變成了閉包的例項欄位,Lambda表示式所表示的匿名委託變成了閉包的例項方法。

 

以上,建立了一個Action,形成了一個"閉包",接下來建立2個Action,形成2個"閉包",看"閉包"的例項欄位是否相互影響?

        static void Main(string[] args)
        {
            Action a = GetAction();
            Action b = GetAction();
            Console.Write("第一次呼叫a,i的值=");
            a();
            Console.WriteLine();
            Console.Write("第二次呼叫a,i的值=");
            a();
            Console.WriteLine();
            Console.Write("第一次呼叫b,i的值=");
            b();
            Console.WriteLine();
        }
        static Action GetAction()
        {
            Action result = null;
            int i = 0;
            result = () => Console.Write(i++);
            return result;          
        }

15

以上,雖然是把同一個GetAction方法分別賦值給了Action委託,但GetAction方法分別在不同的"閉包"內,當呼叫委託執行GetAction方法的時候,各自對閉包內的例項欄位i自增1並列印,相互間不影響。

 

總結:每個Action都有自己的"閉包",並且"閉包"間互不影響。

 

 

“委託、Lambda表示式、事件系列”包括:

委託、Lambda表示式、事件系列01,委託是什麼,委託的基本用法,委託的Method和Target屬性

委託、Lambda表示式、事件系列02,什麼時候該用委託

委託、Lambda表示式、事件系列03,從委託到Lamda表示式

委託、Lambda表示式、事件系列04,委託鏈是怎樣形成的, 多播委託, 呼叫委託鏈方法,委託鏈異常處理

委託、Lambda表示式、事件系列05,Action委託與閉包

委託、Lambda表示式、事件系列06,使用Action實現觀察者模式,體驗委託和事件的區別

委託、Lambda表示式、事件系列07,使用EventHandler委託

相關文章