多播委托与事件

1.多播委托定义以及使用

我们通过委托可以实现把方法作为参数,传递给委托执行。同样,我们的委托也可以依次执行多个方法,此时就需要我们的多播委托了。

没有接触多播委托之前,我们调用多个方法的委托定义如下:

          ReturnWithPara para = new ReturnWithPara(ShowId); //当前类的方法
                ReturnWithPara para1 = ShowId;//当前类的方法
                ReturnWithPara para2 = (i, name) => Console.WriteLine("编号:" + i + ",名称:" + name);//lamda表达式
                ReturnWithPara para3 = new OtherClass().ShowId;//外部类的实例方法
                ReturnWithPara para4 = OtherClass.ShowIdStatic;//外部类的静态方法

当使用多播委托后,我们的代码如下:

          //多播委托方式:通过+=添加方法,形成方法链,并按顺序执行
                ReturnWithPara para = new ReturnWithPara(ShowId); //当前类的方法
                para += ShowId;
                para += (i, name) => Console.WriteLine("编号:" + i + ",名称:" + name);//lamda表达式
                para += new OtherClass().ShowId;//外部类的实例方法
                para += OtherClass.ShowIdStatic;//外部类的静态方法
                para.Invoke(21, "张三");

多播委托方式:通过+=添加方法,形成方法链,Invoke时按添加顺序执行方法.

同样,我们也可以通过-=给委托实例移除方法,如下所示

                //多播委托移除:通过-=给委托实例移除方法,从方法链的尾部开始匹配,遇到第一个完全吻合的移除,并且只移除一次
                para -= ShowId;
                para -= (i, name) => Console.WriteLine("编号:" + i + ",名称:" + name);//lamda表达式
                para -= new OtherClass().ShowId;//外部类的实例方法
                para -= OtherClass.ShowIdStatic;//外部类的静态方法
                para.Invoke(22, "李四");

注意:多播委托移除:通过-=给委托实例移除方法,从方法链的尾部开始匹配,遇到第一个完全吻合的移除,并且只移除一次

以上代码执行后,我们会发现(i, name) => Console.WriteLine("编号:" + i + ",名称:" + name);和new OtherClass().ShowId;没有移除成功。

这是因为:1.每个lamda表达式在编译时都会生成一个不同的方法,但都属于同一个实例。因此lamda表达式不能移除,所以不推荐lamda表达式写法

        2.new OtherClass().ShowId不能移除是因为是两个不同的匿名对象,所以不能移除

多播委托不能直接调用异步方法(eg:para.BeginInvoke(22,"李四");),如果要使用异步调用,可以采取下面的方法

          ReturnWithPara para = new ReturnWithPara(ShowId); //当前类的方法
                para += ShowId;
                para += (i, name) => Console.WriteLine("编号:" + i + ",名称:" + name);//lamda表达式
                para += new OtherClass().ShowId;//外部类的实例方法
                para += OtherClass.ShowIdStatic;//外部类的静态方法
                foreach (var item in para.GetInvocationList())
                {
                    para.BeginInvoke(21, "张三", null, item);
                }

在上述代码中,我们通过para.GetInvocationList()方法获取所有的执行方法,循环执行每个的异步方法。

注意:由于多播委托执行带返回值的多播委托时,只返回最后一个方法的返回值,所以多播委托不适用于带返回值的委托。

多播委托常用的场景就是观察者模式。如下代码所示

我们首先定义个Cat,Chicken等实体,每个实体都有一个方法事件。Cat的Miao方法会引起连锁反应,会引起鸡叫,baby哭泣等等,Cat类里面定义了个委托Cathander

 public class Cat
    {
        public void Miao()
        {
            Console.WriteLine("Miao····");
            if (Cathander != null)
            {
                Cathander.Invoke();
            }
        }
        public Action Cathander;
}

在程序的上级调用中如下所示:

          Cat cat = new Cat();
                cat.Cathander += new Chicken().Wo;
                cat.Cathander += new Dog().Wang;
                cat.Cathander += new Baby().Cry;
                cat.Cathander += new Father().Roar;
                cat.Cathander += new Brother().Turn;
                cat.Cathander += new Mother().Run;
                cat.Miao();
cat.Miao()执行后就会调用绑定了事件的多播委托Cathander

 使用观察者设计模式的好处:保证了Cat类的稳定,需求变更时,只需修改最上层调用即可,无需关注cat代码

2.事件

定义:多播委托的定义前面加一个event关键字,如下所示,我们子啊Cat里面定义了一个事件CathanderEvent

public event Action CathanderEvent;
        public void MiaoEvent()
        {
            Console.WriteLine("Miao····");
            if (CathanderEvent != null)
            {
                CathanderEvent.Invoke();
            }
        }

上层代买调用执行:

          Cat cat = new Cat();
                cat.CathanderEvent += new Chicken().Wo;
                cat.CathanderEvent += new Dog().Wang;
                cat.CathanderEvent += new Baby().Cry;
                cat.CathanderEvent += new Father().Roar;
                cat.CathanderEvent += new Brother().Turn;
                cat.CathanderEvent += new Mother().Run;
                cat.MiaoEvent();

观察以上代码,和多播委托很类似。

3.多播委托与事件的区别和联系

从以上部分介绍,我们是否感觉多播委托和事件很类似,那为什么还要用事件呢?

事件加了权限限制,在事件定义的外部类(或定义类的子类)中,只允许外部+=和-=。不能进行类似cat.CathanderEvent=null之类的赋值,这种赋值只能在定义事件的内部类中才可以。

委托与事件的区别和联系:委托是一个类,而事件是委托的一个实体对象

4.事件的运用场景

 事件应用很广泛,主要包含:

1.服务器的控件的点击事件、下拉事件等。

2、Webform页面声明周期,各种页面级事件(pre_init、page_load、application_start)。

3.请求级事件:经常注册module 等等

01-26 01:44