zoukankan      html  css  js  c++  java
  • 从事件来看委托

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

  • 相关阅读:
    表格标签
    常用标签
    标签笔记
    基础标签与格式
    态度!
    如何修改数据 练习
    增删查练习
    登陆注册练习
    PHP 数据访问
    PHP 基础知识测试题 答案分析
  • 原文地址:https://www.cnblogs.com/simen-tan/p/6847594.html
Copyright © 2011-2022 走看看