前言
本文已更新至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); } }