與前面的可空類型是一樣的,匿名方法也是C# 2.0里面提出來的。
1 匿名方法
? ?1.1 什么是匿名方法?
? ? ? ? 顧名思義,就是沒有名稱的方法,因為沒有名稱,匿名方法只能在函數定義(匿名方法是把方法的實現和定義嵌套在了一起)的時候被調用,在其他任何情況下都不能被調用。對于編譯器來說,匿名方法并不是沒有名字的,編譯器在編譯匿名方法時會為其生成一個方法名,下面可以通過IL代碼來證明。
? ?
1 namespace 匿名方法 2 { 3 class Program 4 { 5 private delegate void VoteDelegate(string name); 6 static void Main(string[] args) 7 { 8 //使用匿名方法來實例化委托對象 9 VoteDelegate voteDelegate = delegate(string name) 10 { 11 Console.WriteLine("昵稱為:{0} 來幫我提高啦",name); 12 }; 13 //通過調用委托來回調Vote方法,這是隱式調用方式 14 voteDelegate("Hong"); 15 Console.ReadKey(); 16 } 17 } 18 }
? ? 從以上的代碼可以看出,委托對象可用匿名方法來實例化的。當并不能一概而論,匿名方法也有它本身的缺點——不能在其他地方被調用,即不具有復用性。而且,匿名方法會自動形成“閉包”。當一個函數(這里成為外部函數)包含對另一個函數(內部函數)的調用時,或當內部函數使用了外部函數的變量時,都會形成閉包。閉包可能會延長外部變量的生命周期。所以如果委托包裝的方法相對簡單,并且該方法在其他地方的調用頻率較低,你就可以考慮用匿名方法來實例化委托對象。
? ??
? ? 1.2 對變量捕捉過程的剖析
? ? ? ? ?變量被匿名方法捕獲后,變量的生命周期會被延長,就是說對于一個被捕獲的變量而言,只要還有任何委托實例在引用它,它就一直存在,就不會在部分委托實例調用結束后被垃圾回收釋放掉。下面通過代碼來解釋:
1 class Program 2 { 3 private delegate void ClosureDelegate(); 4 static void Main(string[] args) 5 { 6 ClosureDelegate test = CreateDelegateInstance(); 7 test(); 8 Console.ReadKey(); 9 } 10 11 private static ClosureDelegate CreateDelegateInstance() 12 { 13 //外部變量 14 int count = 1; 15 ClosureDelegate closureDelegate = delegate() 16 { 17 Console.WriteLine(count); 18 count++; 19 }; 20 closureDelegate(); 21 return closureDelegate; 22 } 23 }
結果:
? ? 我并不知道大家對這個結果是否感到驚訝,第一次輸出1是正常的,但第二個輸出2確實比較奇妙的。大家可能會認為應該拋出異常才對,因為count在棧上的生命周期已經結束。但結果確實是輸出2,是不會錯的,我們可以倒推原因,使用匿名方法時,編譯器會創建一個額外的類來容納變量,此時count變量會被分配到堆上的。CreateDelegateInstance方法有該類的一個實例引用,所以此時匿名方法捕捉到的是變量count的一個引用,而不是真真的值。在這個過程中,匿名方法延長了變量count的生命周期。
? ? 對于匿名方法捕捉到的變量,編譯器會額外創建一個類來容納它們。
? ??
? ? 由上圖可知,我們并沒有在源碼中定義<>c_DisplayClass2_0類。這個類就是編譯器創建來容納捕獲的變量count的,該類還容納了CreateDelegateInstance的方法。