事件是基於委託,爲委託提供了一種發佈/訂閱機制,在dotNet處處都能看到事件,一個簡單的例子就是在windows應用程序中,Button類提供了Click事件,這類事件就是委託,觸發Click事件時調用的處理程序方法須要定義,其參數也是由委託類型定義的,事件模型能夠用下圖簡要說明。windows
在這個模型中,事件的響應者經過訂閱關係直接關聯在事件擁有者的事件上,咱們把這種事件模型或者CLR事件模型。由於CLR事件本質上是一個委託實例,咱們暫且模仿CLR屬性的說法,把CLR事件定義爲一個委託類型實例的包裝器。安全
下面一個示例,事件用於鏈接CarDealer類和Consumer類,CarDealer類提供了一個新車到達時的觸發事件,Consumer類訂閱該事件,以得到新車到達的通知。從CarDealer類開始,它基於事件提供一個訂閱,CarDealer類用event關鍵字定義了類型爲EventHandler<CarInfoEventArgs>的NewCarInfo事件,在NewCar()中,經過調用RaiseNewCarInfo方法觸發NewCarInfo事件,這個方法的實現檢查委託是否爲空,若是不爲空,就引起事件。app
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.ide
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事件,經過「+=」建立一個訂閱,而後經過「-=」取消訂閱this
var dealer = new CarDealer(); var myCar = new Consumer("MyCar"); dealer.NewCarInfo += myCar.NewCarIsHere; dealer.NewCar("OneCar"); dealer.NewCarInfo -= myCar.NewCarIsHere; dealer.NewCar("OtherCar");
經過事件,直接鏈接到發佈程序和偵聽器,但垃圾回收器有個問題,若是偵聽器不在直接引用,發佈程序就仍有一個引用,垃圾回收器不能清空偵聽器佔用的內存,由於發佈程序仍有一個引用,會針對偵聽器觸發事件,這種強鏈接的模式能夠經過弱引用事件模式來解決,即便用WeakEventManager做爲發佈程序和偵聽器之間的中介。spa
更改Consumer的代碼實現IWeakEventListener接口code
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; } }
更改訂閱事件的代碼orm
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類blog
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#編譯器會識別這個類,並使用其委託語法。排序