发布者和订阅者模式----8

49 阅读4分钟

事件

场景需求:当某个特定的程序事件发生时,程序的其它部分可以得到该事件已经发生的通知。 发布者/订阅者模式可以满足该需求。

发布者/订阅者模式描述

  1. 发布者类定义一系列订阅者类感兴趣的事件。
  2. 订阅者类可以在事件上注册,以便这些事件发生时发布者可以通知它们。
  3. 注册是通过由订阅者类向发布者类提供一个方法来完成的。
  4. 当事件发生时,发布者触发事件,然后执行订阅者所提交的所有方法。

image.png

由订阅者提供的方法,称为回调方法,因为发布者通过执行这些方法来“往回调用订阅者的方法”,也可称为事件处理程序,因为它们是为处理事件而调用的代码。

发布者/订阅者的四大主体

发布者:发布事件的类或结构,其他类可以在事件发生时得到通知。

订阅者:注册并在事件发生时得到通知的类或结构。

事件处理程序(回调方法):由订阅者注册到事件上的方法,在事件触发时执行。可定义在不同的类和结构中。

触发事件:当事件触发时,所有在它上面注册的方法都会被依次执行。

事件和委托的关系

事件就像是用于某种特殊用途的委托。事件包含了一个私有委托。

image.png

事件提供了对其私有委托的结构化访问,类似于普通类中属性对字段的封装。

对于事件,我们只可以添加、删除和调用事件处理程序。

事件触发时,它通过委托来依次调用所有的事件处理程序。

事件五大组件

委托类型声明:事件和事件处理程序的签名和返回类型必须匹配,通过委托来描述。

image.png

image.png

事件实例

发布者类:

 internal class TeaShop
    {
        private string shopName;
        public TeaShop(string externShopNme)
        { 
            this.shopName = externShopNme;
        }

        //声明事件
        //事件在发布者类中声明(发布者类)
        //事件基于委托来声明,因此需要知道委托类型的名字
        //事件声明为public,这样其它类或结构才可以在它上面注册事件处理程序
        //可在一条语句中声明多个事件,事件可以声明为静态的。

        //事件和委托的区别
        //事件不是一个类型,和方法、属性一样,事件是类或结构中的一个成员
        //不可以在一段可执行代码中声明事件(函数内部)。
        //事件声明必须在类或结构中,和其它成员一样。
        //事件成员自动隐式初始化为null

        //1.事件声明----新茶事件---基于委托定义
        public  event TeaDelegate NewTeaEvent;

        //触发事件,写在发布者类
        //事件本身只是保存了需要被调用的事件处理程序,如果事件没有被触发,什么都不会发生。我们需要
        //在合适的时候有代码来做这件事情

        //触发事件之前,将事件成员和null比较,从而确定事件上是否包含事件处理程序。
        //触发事件的语法与调用方法一样,事件名(参数列表)
        //新茶上市

        //2.触发事件的代码
        public void NewTeaCome(string teaName, float teaPrice)
        {
            Console.WriteLine("{0}:好消息好消息!新茶上市。品种:{1},价格:{2}",this.shopName,teaName,teaPrice);
            //判断事件是否为空
            if (NewTeaEvent != null) 
            {
                NewTeaEvent(teaName, teaPrice);
            }
        }
    }

订阅者类

internal class Consumer
    {
        private string name;
        public Consumer(string externName)
        { 
            this.name = externName;
        }
        /// <summary>
        /// 订阅者类提供了事件处理程序
        /// </summary>
        /// <param name="teaName">茶名</param>
        /// <param name="teaPrice">茶价</param>
        //方法,消费者得到新茶的相关信息
        //3.触发事件声明 与委托类型签名保持一致
        public void GetNewTeaInfo(string teaName, float teaPrice)
        {
            Console.WriteLine("{0}:收到消息,新茶上市!品种:{1}  价格:{2}",this.name,teaName,teaPrice);
        }    
    }

主程序类

//3.茶叶委托类型声明
    public delegate void TeaDelegate(string teaName, float teaPrice);
    internal class Program
    {
        static void Main(string[] args)
        {
            TeaEventTest();
            Console.ReadLine(); 
        }

        //方法,茶事件测试
        //订阅事件
        //订阅者向事件上添加事件处理程序(注册)
        //事件处理程序必须和事件的委托拥有相同的签名和返回值类型
        //使用“+=”运算符来实现注册和订阅
        static void TeaEventTest()
        {
            //创建发布者类
            TeaShop teaShop = new TeaShop("东城茶叶专卖");
            Consumer consumer = new Consumer("丁原");
            Consumer consumer1 = new Consumer("叶修");

            //4.注册事件、订阅事件
            teaShop.NewTeaEvent += consumer.GetNewTeaInfo;
            teaShop.NewTeaEvent += consumer1.GetNewTeaInfo;

            //5.触发事件 新茶上市
            teaShop.NewTeaCome("碧螺春", 200);

           
        }
    }