世界杯車輪戰開始了,連通三天,基本進入世界杯狀態??辞蛞膊荒芡送婕夹g,這次打算把接觸c#以來的點滴總結起來,讓原本模糊的概念清晰起來,博友們一起來吧!
[閉包]接觸這個詞的第一感覺就是晦澀難懂,下面我們就來啃一啃。
第一次接觸閉包是在js里,先來看代碼段[1]:

1 function a() { 2 var i = 0; 3 function b() { 4 alert(++i); 5 } 6 return b; 7 } 8 var c = a(); 9 c(); js的閉包很簡單的代碼,細心觀察會發現變量i的作用域是在方法a中,也就是說出了方法a后變量i就不起作用了,可代碼中的i依然活躍在方法c中,是不是違背了程序的基本規則呢?球出界了隊員還在踢。
先來啃啃[閉包]的正經概念,[閉包(Closure)]就是引用了自由變量的表達式,通常是函數(也就是代碼段[1]中的函數b)。它的特點是被引用的自由變量將和這個函數一同存在,即使已經離開了創造它的環境也不例外,通俗的講就是大家常說的閉包延長了變量的生命期。對照代碼段[1],清晰些了嗎?
下面來看c#版的,代碼段[2]:

1 public class PRogram 2 { 3 public static Action funcB() 4 { 5 Console.WriteLine("funcB Begin.."); 6 int i = 0; 7 i++; 8 Console.WriteLine("funcB:" + i); 9 Action action = () =>10 {11 Console.WriteLine("funcA:" + i);12 };13 i = 100;14 Console.WriteLine("funcB:" + i);15 Console.WriteLine("funcB End..");16 return action;17 }18 static void Main()19 {20 var action = funcB();21 action();22 Console.ReadKey();23 }24 }c#的閉包 寫法1

1 public class Program 2 { 3 public static async Task funcA(Action callback) 4 { 5 //停留5秒,緩下節奏 6 await Task.Delay(5000); 7 Console.WriteLine("funcA continue.."); 8 callback(); 9 }10 public static void funcB()11 {12 Console.WriteLine("funcB Begin..");13 int i = 0;14 i++;15 Console.WriteLine("funcB:" + i);16 funcA(() =>17 {18 Console.WriteLine("funcA:" + i);19 });20 i = 100;21 Console.WriteLine("funcB:" + i);22 }23 static void Main()24 {25 funcB();26 Console.WriteLine("funcB End..");27 Console.ReadKey();28 }29 }c#的閉包 寫法2兩個寫法目的一樣,就是想勾起大家的所有疑問。代碼段[2]的運行結果是什么呢?為什么是這樣的結果呢?[閉包]真的延長了變量的生命期嗎?相信熟悉的人是清楚的,我們要做的是繼續深挖。
我們都懂這只是語法糖,它并沒有違背程序的基本規律。下面就抄家伙(.NET Reflector)來窺窺究竟。

圖[1] 圖[2]

圖[3]

圖[4]

圖[5]
一下上了5張圖,不要慌,慢慢來。
圖[1]是Reflector中Program類的字段、方法、類等的名稱列表。我們注意到,除去我們代碼中定義的,編譯器還自動生成了一個類:c__DisplayClass1 ?。?/p>
圖[2]是編譯器自動生成的類c__DisplayClass1中的一個方法<funcB>b__0的定義,其實就是funcB方法中的那個匿名函數 ()=>{Console.WriteLine("funcA:" + i);} 的實現;
圖[3]是編譯器自動生成的類c__DisplayClass1的實現的IL代碼,請注意方法<funcB>b__0和公共變量i
圖[4]、圖[5]是funB方法的IL代碼,每一段代表的啥意思我都大概做了標注??梢钥吹剑涸诜椒ǖ囊婚_始,編譯器就初始化了c__DisplayClass1類的一個實例,之后對于變量i的操作,在IL中其實就是對于起初初始化的那個全局的c__DisplayClass1類實例中的變量i的操作,所以說[閉包]延長了變量的生命期是假象,其實我們一直在操作一個全局的類實例的變量。
原理基本清楚了,下面我們來自己動手模仿一下編譯器做的事。
代碼段[3]:

1 public class Program 2 { 3 //定義一個全局的c__DisplayClass1類型的變量。 4 static c__DisplayClass1 displayCls; 5 /// <summary> 6 /// 這就是類似于編譯器的那個自定義類<>c__DisplayClass1 7 /// </summary> 8 sealed class c__DisplayClass1 9 {10 public int i;11 12 public void b_0()13 {14 Console.WriteLine("funcA:" + i);15 }16 }17 public static Action funcB()18 {19 displayCls = new c__DisplayClass1();20 Console.WriteLine("funcB Begin..");21 displayCls.i = 0;22 displayCls.i++;23 Console.WriteLine("funcB:" + displayCls.i);24 Action action = displayCls.b_0;25 displayCls.i = 100;26 Console.WriteLine("funcB:" + displayCls.i);27 Console.WriteLine("funcB End..");28 return action;29 }30 static void Main()31 {32 var action = funcB();33 action();34 Console.ReadKey();35 }36 }類似閉包編譯器費盡心思給我們做了一個語法糖,讓我們的編程更加輕松優雅。
只上代碼,代碼段[4]:

1 public class Program 2 { 3 public static List<Action> funcB() 4 { 5 List<Action> list = new List<Action>(); 6 Console.WriteLine("funcB Begin.."); 7 int i = 0; 8 i++; 9 Console.WriteLine("funcB:" + i);10 Action action1 = () =>11 {12 Console.WriteLine("funcA:" + i);13 i = 200;14 };15 Action action2 = () =>16 {17 Console.WriteLine("funcA:" + i);18 };19 i = 100;20 Console.WriteLine("funcB:" + i);21 Console.WriteLine("funcB End..");22 list.Add(action1);23 list.Add(action2);24 return list;25 }26 static void Main()27 {28 var action = funcB();29 action[0]();30 action[1]();31 Console.ReadKey();32 }33 }終極測試運行結果是什么呢?自己動手,豐衣足食。
就到這兒吧,有問題的地方希望各位指正,敬禮!
新聞熱點
疑難解答