從事件來看委托
事件是基於委托,為委托提供了一種發布/訂閱機制,在dotNet到處都能看到事件,一個簡單的例子就是在windows應用程序中,Button類提供了Click事件,這類事件就是委托,觸發Click事件時調用的處理程序方法需要定義,其參數也是由委托類型定義的,事件模型可以用下圖簡要說明。
在這個模型中,事件的響應者通過訂閱關系直接關聯在事件擁有者的事件上,我們把這種事件模型或者CLR事件模型。因為CLR事件本質上是一個委托實例,我們暫且模仿CLR屬性的說法,把CLR事件定義為一個委托類型實例的包裝器。
下面一個示例,事件用於連接CarDealer類和Consumer類,CarDealer類提供了一個新車到達時的觸發事件,Consumer類訂閱該事件,以獲得新車到達的通知。從CarDealer類開始,它基於事件提供一個訂閱,CarDealer類用event關鍵字定義了類型為EventHandler<CarInfoEventArgs>的NewCarInfo事件,在NewCar()中,通過調用RaiseNewCarInfo方法觸發NewCarInfo事件,這個方法的實現檢查委托是否為空,如果不為空,就引發事件。
public class CarInfoEventArgs : EventArgs { public string Car { get; private set; } public CarInfoEventArgs(string car) { this.Car = car; } } public class CarDealer { public event EventHandler<CarInfoEventArgs> NewCarInfo;public void NewCar(string car) { Console.WriteLine("CarDealer,new car {0}", car); RaiseNewCarInfo(car); } protected virtual void RaiseNewCarInfo(string car) { NewCarInfo?.Invoke(this, new CarInfoEventArgs(car)); } }
Consumer類用作事件偵聽器,這個類訂閱了CarDealer類的事件,並定義了NewCarIsHere方法,該方法滿足EeventHandler<CarInfoEventArgs>委托的要求,其參數類型是object和CarInfoEventArgs.
public class Consumer { private string name; public Consumer(string name) { this.name = name; } public void NewCarIsHere(object sender, CarInfoEventArgs e) { Console.WriteLine($"{name}:car {e.Car} is new"); } }
需要連接事件發布程序和訂閱器,為此使用CarDealer類的NewCarInfo事件,通過“+=”創建一個訂閱,然後通過“-=”取消訂閱
var dealer = new CarDealer(); var myCar = new Consumer("MyCar"); dealer.NewCarInfo += myCar.NewCarIsHere; dealer.NewCar("OneCar"); dealer.NewCarInfo -= myCar.NewCarIsHere; dealer.NewCar("OtherCar");
通過事件,直接連接到發布程序和偵聽器,但垃圾回收器有個問題,如果偵聽器不在直接引用,發布程序就仍有一個引用,垃圾回收器不能清空偵聽器占用的內存,因為發布程序仍有一個引用,會針對偵聽器觸發事件,這種強連接的模式可以通過弱引用事件模式來解決,即使用WeakEventManager作為發布程序和偵聽器之間的中介。
更改Consumer的代碼實現IWeakEventListener接口
public class Consumer : IWeakEventListener { private string name; public Consumer(string name) { this.name = name; } public void NewCarIsHere(object sender, CarInfoEventArgs e) { Console.WriteLine("{0}: car {1} is new", name, e.Car); } bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e) { NewCarIsHere(sender, e as CarInfoEventArgs); return true; } }
更改訂閱事件的代碼
var dealer = new CarDealer(); var myCar = new Consumer("MyCar"); WeakEventManager<CarDealer, CarInfoEventArgs>.AddHandler(dealer, "NewCarInfo", myCar.NewCarIsHere); //dealer.NewCarInfo += myCar.NewCarIsHere; dealer.NewCar("OneCar"); WeakEventManager<CarDealer, CarInfoEventArgs>.RemoveHandler(dealer, "NewCarInfo", myCar.NewCarIsHere); //dealer.NewCarInfo -= myCar.NewCarIsHere; dealer.NewCar("OtherCar");
WPF使用弱事件模式和事件管理器,在dotNet中委托是類型安全的類,它定義了返回類型和類型參數的類型,委托不僅半酣方法的引用,也可以包含對多個方法引用,lambda表達式與委托直接相關,當參數是委托類型時,就可以直接使用lambda表達式實現委托引用的方法,除了為每個參數和返回類型定義一個新委托之外,還可以使用Action<T>和Func<T>委托,泛型Action<T>委托表示引用一個void返回類型的方法,Func<T>允許調用帶返回類型的方法,下面用委托實現經典的冒泡排序,定義一個實體類,在類中定義一個返回bool類型的靜態方法,定義一個實現排序方法的BubbleSorter類
Employee[] employees = { new Employee("Bugs Bunny", 20000), new Employee("Elmer Fudd", 10000), new Employee("Daffy Duck", 25000), new Employee("Wile Coyote", 1000000.38m), new Employee("Foghorn Leghorn", 23000), new Employee("RoadRunner", 50000) }; BubbleSorter.Sort(employees, Employee.CompareSalary); foreach (var employee in employees) { Console.WriteLine(employee); } public class BubbleSorter { static public void Sort<T>(IList<T> sortArray, Func<T, T, bool> comparison) { bool swapped = true; do { swapped = false; for (int i = 0; i < sortArray.Count - 1; i++) { if (comparison(sortArray[i + 1], sortArray[i])) { T temp = sortArray[i]; sortArray[i] = sortArray[i + 1]; sortArray[i + 1] = temp; swapped = true; } } } while (swapped); } } public class Employee { public Employee(string name, decimal salary) { this.Name = name; this.Salary = salary; } public string Name { get; private set; } public decimal Salary { get; private set; } public override string ToString() { return string.Format("{0}, {1:C}", Name, Salary); } public static bool CompareSalary(Employee e1, Employee e2) { return e1.Salary < e2.Salary; } }
實際上,定義一個委托是指定義一個新類,委托實現為派生自基類的System.MulticastDelegate的類,System.MulticastDelegate又派生自基類System.Delegate,C#編譯器會識別這個類,並使用其委托語法。
從事件來看委托