首先感謝園友的指定,後續的文章一定會多碼多想,出來的文章才有說服力。那今天接上篇我們來聊一聊匿名函數,對於匿名函數,我們知道使用delegate關鍵字,那我們來需要知道匿名函數在變量是的處理方式,先說兩個術語,外部變量和捕獲的外部變量,可以看出"捕獲的外部變量=外部變量+捕獲了",這個捕獲顧名思義就是在匿名函數中使用了這個變量。
外部變量:指在一個包含匿名方法的作用域內的變量或者參數,在類的實例成員內部的匿名方法,this也是認為是一個外部變量。
捕獲的外部變量:它是指在匿名方法中使用的外部變量。
代碼如下
1 static void Main(string[] args) 2 { 3 //x和y稱為外部變量 4 int x = 0, y = 1; 5 //在匿名方法中使用到了x,則x稱為捕獲的外部變量 6 Action<int> ac = delegate (int n) { Console.WriteLine(x); }; 7 8 //小結:x、y和匿名方法都在Main函數的作用域內,也可以擴展到類的作用域及命名空間的作用域 9 10 Console.ReadKey(); 11 }
再來說下匿名方法捕獲變量的行為,可以看到在匿名方法中我們訪問到了局部變量x,請注意,並不是僅僅訪問到了x的值,而是在匿名類型中使用一個類型實例引用到了變量x,對於x的改變,因為是引用,所以總能使用這個類型實例訪問到,如
1 long x1 = 11, y1 = 12; 2 Action<long> ac1 = delegate (long l) { Console.WriteLine(x1); }; 3 ac1(1L); //打印11 4 x1 = y1; 5 ac1(1L); //打印12
參數long l這裡沒有使用到,不過這裡的參數不是上面所說的外部變量,因為它確實是匿名方法的參數
1 static void Debug(int x) { 2 Action<int> a = delegate (int y) { Console.WriteLine(x); }; 3 }
上面的x就是術語中說的外部變量,分清定義就應該沒問題了吧。
關於變量的生存周期,可以就只在一個作用域內,當代碼執行完這個作用域,該作用域內的變量也會被銷毀,但使用匿名方法可以延長變量的生存周期。
1 static void Main(string[] args) 2 { 3 GetLen gl = GetMethod(); 4 gl("s"); //打印00s 5 gl("s"); //打印0000s 6 7 Console.ReadKey(); 8 } 9 10 public delegate int GetLen(string s); 11 static GetLen GetMethod() 12 { 13 string temp = "0"; 14 return delegate (string s) { 15 temp = String.Concat(temp, temp); 16 s = String.Concat(temp, s); 17 Console.WriteLine(s); 18 return s.Length; 19 }; 20 }
看出使用GetMethod返回一個委托,這裡使用匿名函數(因為匿名函數就是對應簽名的委托),在正常理解下temp在GetMethod作用域內,當離開作用域外,這個變量會銷毀,但說過匿名函數會使用一個類型實例引用這個變量,則這個變量不會銷毀,只有當匿名函數銷毀(也就是委托)才會跟著銷毀,從而延長了變量的作用域,而且對於temp變量的操作也會直接反應在實例引用的變量上,如第一次調用gl("s"),temp="00",第二次調用時,temp="0000"。
最後說下有點繞的東西,就是變量的實例化在匿名函數中的訪問規則,不過個人感覺這個還真是不很繞,還算是比較好理解的。看下代碼。
1 GetLen[] a = { }; 2 int xx = 0; 3 for (int i = 0; i < 3; i++) 4 { 5 int xxx = i; 6 a[i] = delegate (string s) { 7 xx++; 8 xxx++; 9 return xx + xxx; 10 }; 11 }
a是一個委托數組,對於數組中每一個委托都共享一個xx實例引用,而每一個委托都各自擁有一個xxx實例引用(xxx對應不同的委托是不同的),這是因為在循環中,每一次的循環都實例化了xxx,則對於各個委托都有一個全新的xxx實例引用,而xx則是在循環之外實例化的,則每個委托共享一個實例引用。當然在實際的使用過程中,不可能那麼簡單,那麼要我們開動大腦,好好區別哪一個共享的,哪一個是獨自引用的。
請斧正。