首页 > 代码库 > 从事件来看委托

从事件来看委托

事件是基于委托,为委托提供了一种发布/订阅机制,在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#编译器会识别这个类,并使用其委托语法。

 

从事件来看委托