前言
本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html 。本文主要學習記錄以下內容:
建議38、小心閉包中的陷阱
建議39、瞭解委託的實質
建議40、使用event關鍵字對委託施加保護
建議41、實現標準的事件模型
建議38、小心閉包中的陷阱
首先我們先來看一段程式碼:
class Program { static void Main(string[] args) { List<Action> list = new List<Action>(); for (int i = 0; i < 5; i++) { Action t = () =>Console.WriteLine(i.ToString()); list.Add(t); } foreach (Action t in list) { t(); } Console.ReadLine(); } }
你設想的結果或許是0,1,2,3,4
但沒想到執行後結果如下
通過IL可以檢視程式碼,組合後大致程式碼如下:
public class TempClass { public int i; public void TempFunc() { Console.WriteLine(i.ToString()); } } class Program { static void Main(string[] args) { List<Action> list = new List<Action>(); TempClass tempClass = new TempClass(); for (tempClass.i = 0; tempClass.i < 5; tempClass.i++) { Action t = tempClass.TempFunc; list.Add(t); } foreach (Action t in list) { t(); } Console.ReadLine(); } }
當然執行後結果還是5,5,5,5,5
其實這段程式碼所演示的就是一個閉包物件。所謂的閉包物件,指的是上面這種情形中的TempClass物件,如果匿名方法(Lambda表示式)引用了某個區域性變數,編譯器就會自動將該引用提升到該閉包物件中,即將for迴圈中的變數i修改成了引用閉包物件的公共變數i。這樣一來,即使程式碼執行後離開了原區域性變數i的作用域(如for迴圈),包含該閉包物件的作用域也還存在。
下面簡單修改一下之前的程式碼
class Program { static void Main(string[] args) { List<Action> list = new List<Action>(); for (int i = 0; i < 5; i++) { int temp = i; Action t = () => Console.WriteLine(temp.ToString()); list.Add(t); } foreach (Action t in list) { t(); } Console.ReadLine(); } }
執行結果如下:
建議39、瞭解委託的實質
http://www.cnblogs.com/aehyok/archive/2013/03/22/2976356.html這裡有我之前對委託的簡單的學習過程,雖然在工作中很少用,幾乎就沒用。不過還是拿來學習學習。
理解委託需要把握兩個點:
1、委託是方法指標。
2、委託就是一個類。當對其進行例項化的時候,要將引用方法作為它建構函式的引數。
建議40、使用event關鍵字對委託施加保護
http://www.cnblogs.com/aehyok/archive/2013/02/22/2922586.html 這也是對於事件的簡單理解學習。
建議41、實現標準的事件模型
我們應該知道微軟為事件模型設定的幾個規範:
1、委託型別的名稱以EventHandler結束。
2、委託原型返回值為void。
3、委託原型具有兩個引數:sender表示事件觸發者,e表示事件引數。
4、事件引數的名稱以EventArgs結束。
public class FileUploadedEventArgs : EventArgs { public int FileProgress { get; set; } } public class FileUploader { public event EventHandler<FileUploadedEventArgs> FileUploaded; public void Upload() { FileUploadedEventArgs e = new FileUploadedEventArgs() { FileProgress=100 }; while (e.FileProgress > 0) { ///傳輸程式碼,省略 e.FileProgress--; if (FileUploaded != null) { FileUploaded(this, e); } } } }
最終進行呼叫的程式碼如下:
class Program { static void Main(string[] args) { FileUploader fileUploader = new FileUploader(); fileUploader.FileUploaded += Progress; fileUploader.Upload(); Console.ReadLine(); } static void Progress(object sender,FileUploadedEventArgs e) { Console.WriteLine(e.FileProgress); } }