一起来阅读《C#图解教程》吧--事件

发布者与订阅者

很多程序都有一个共同的需求,即当一个特定的程序事件发生时,程序的其他部分可以得到该事件已经发生的通知 。可以使用发布者/订阅者模式来满足这种需求,在该模式中,发布者类定义了一系列程序的其他部分可能感兴趣的事件。其他类可以“注册”以便在事件发生的时候可以通知它们。这些订阅者类通过向发布者提供一个方法来“注册”以获取通知。当事件发生时,发布者“触发事件”,然后执行订阅者提交的所有事件。

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

  • 发布者:发布某个事件的类或结构,其他类可以在该事件发生时得到通知。
  • 订阅者:注册并在事件发生时得到通知的类或结构。
  • 事件处理程序:由订阅者注册到事件的方法,在发布者触发事件时执行 事件处理程序方法可以定义在事件所在的类或结构中,也可以定义在不同的类或结构中。
  • 当事件触发时,所有注册到他的方法都会被依次调用。

事件与委托很多部分类似,这是因为事件像是专门用于某种特殊用途的简单委托。委托和事件的行为之所以相似,是因为事件包含了一个私有的委托。
image.png

  • 事件提供了对他的私有控制委托的结构化访问。也就是说,你无法直接访问委托。
  • 事件中可用的操作比委托少,对于事件我们只可以添加、删除、或者调用事件处理程序。
  • 事件在被触发时,它调用委托来依次调用调用列表里的方法。

声明事件

发布者类必须提供事件对象。创建事件比较简单一一只需要委托类型和名字。

//声明事件,其中 
// event 为关键字
// EventHandler 为委托类型
// CounterADozen 为事件名
public event EventHandler CounterADozen;//声明一个事件

public event EventHandler MyEvent1,MyEvent2;//声明多个事件

一个常见的误解是把事件认为是类型,然而它不是。和方法、属性一样,事件是类或结构的成员,这一点引出了几个重要的特性

  • 我们不能在一段可执行代码中声明事件。
  • 它必须声明在类或结构中,和其他成员一样。

事件声明需要委托类型的名字,我们可以声明一个委托类型或使用已存在的。如果我们声明一个委托类型,它必须指定事件保存的方法的签名和返回类型。
BCL声明了一个叫做EventHandler的委托,专门用于系统事件。

订阅事件

订阅者向事件添加事件处理程序。对于一个要添加到事件的事件处理程序来说,它必须具有与事件的委托相同的返回类型和签名。

//事件处理程序为 实例方法
incrementer.CountedADozen += IncrementDozensCount;

//事件处理程序为 静态方法
incrementer.CountedADozen += SClass.Mythod;

//事件处理程序为 委托形式
incrementer.CountedADozen += new EventHandler(cc.CounterHandlerC);

//事件处理程序为 lambda表达式
incrementer.CountedADozen += ()=> DozenCount++;

//事件处理程序为 匿名方法
incrementer.CountedADozen += delegate{DozenCount++;};

触发事件

以下为一个完整的例子:

   class Program
    {
        static void Main(string[] args)
        {
   
            Incrementer incrementer = new Incrementer();
            Dozens dozensCounter = new Dozens(incrementer);

            incrementer.DoCount();
            Console.WriteLine("Number of dozens = {0}",dozensCounter.DozensCount);

            Console.ReadKey();
        }
    }

    delegate void Handler();


    /// <summary>
    /// 发布者
    /// </summary>
    class Incrementer
    {
        public event Handler CountedADozen; // 创建事件并发布

        public void DoCount()
        {
            for (int i = 0; i < 100; i++)
            {
                if (i % 12 == 0 && CountedADozen != null)
                {
                    CountedADozen();
                }
            }
        }
    }

    /// <summary>
    /// 订阅者
    /// </summary>
    class Dozens
    {
        public int DozensCount { get; private set; }

        public Dozens(Incrementer incrementer)
        {
            DozensCount = 0;
            incrementer.CountedADozen += IncrementDozensCount;
        }

        void IncrementDozensCount()
        {
            DozensCount++;
        }
    }


//最后输出为:Number of dozens = 8

标准事件的用法

对于事件的使用.NET框架提供了一个标准模式。事件使用的标准模式的根本就是System命名空间声明的EventHandler委托类型。

public delegate void EventHandler(object sender,EventArgs e)
  • 第一个参数用来保存触发事件的对象的引用。由于是object类型的,所以可以匹配任何类型的实例;
  • 第二个参数用来保存状态信息,指明什么类型适用于该应用程序
  • 返回类型是void

EventArgs设计为不能传递任何数据。它用于不需要传递数据的事件处理程序一一一通常会被忽略。
如果你希望传递数据,必须声明一个派生自EventArgs的类,使用合适的字段来保存需要传递的数据。尽管EventArgs类实际上并不传递数据,但它是使用EventHandler委托模式的重要部分。不管参数使用的实际类型是什么,object类和EventArgs总是基类。这样EventHandler就能提供一个对所有事件和事件处理器都通用的签名,只允许两个参数,而不是各自都有不同签名 。

在上面的例子中,我们使用EventHandler委托来代替Handler。

    class Incrementer
    {
        public event EventHandler CountedADozen; // 创建事件并发布

        public void DoCount()
        {
            for (int i = 0; i < 100; i++)
            {
                if (i % 12 == 0 && CountedADozen != null)
                {
                    CountedADozen(this,null);//这里会有改变
                }
            }
        }
    }

    /// <summary>
    /// 订阅者
    /// </summary>
    class Dozens
    {
        public int DozensCount { get; private set; }

        public Dozens(Incrementer incrementer)
        {
            DozensCount = 0;
            incrementer.CountedADozen += IncrementDozensCount;
        }

        void IncrementDozensCount(object o,EventArgs e)//这里会增加参数
        {
            DozensCount++;
        }
    }

可以看到,改变的地方是我们在事件的参数这里要跟EventHandler委托保持一致。

posted @ 2022-08-09 18:07  LemonInCup  阅读(66)  评论(0编辑  收藏  举报