找回密码
 立即注册
快捷导航

[C#] 事件 event

[复制链接]
茎肛互撸娃 2023-8-1 00:25:11 | 显示全部楼层

事件

发布者/订阅者模式

很多程序都有一个共同的需求,即当一个特定的程序事件发生时,程序的其他部分可以得到该事件已经发生的通知。

发布者/订阅者模式(publisher/subscriberpattern)可以满足这种需求。在这种模式中,发布者类定义了一系列程序的其他部分可能感兴趣的事件。其他类可以“注册”,以便在这些事件发生时收到发布者的通知。这些订阅者类通过向发布者提供一个方法来“注册”以获取通知。当事件发生时,发布者“触发事件”,然后执行订阅者提交的所有事件。

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

事件 event9325 作者:茎肛互撸娃 帖子ID:564

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

之前介绍了委托。事件的很多部分都与委托类似。实际上,事件就像是专门用于某种特殊用途的简单委托。委托和事件的行为之所以相似,是有充分理由的。事件包含了一个私有的委托

事件 event8210 作者:茎肛互撸娃 帖子ID:564

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

下图演示了一个叫作Incrementer的类;它按照某种方式进行计数。

  • Incrementer定义了一个CountedADozen事件,每次累积到12个项时将会触发该事件
  • 订阅者类Dozens和SomeOtherclass各有一个注册到CountedADozen事件的事件处理程序
  • 每当触发事件时,都会调用这些处理程序。

事件 event5077 作者:茎肛互撸娃 帖子ID:564

5部分组成

上图需要由5部分组成:

  • 委托类型声明: 事件和事件处理程序必须有共同的签名和返回类型,它们通过委托类型进行描述。
  • 事件处理程序声明: 订阅者类中会在事件触发时执行的方法声明。它们不一定是显式命名的方法,还可以是匿名方法或Lambda表达式。
  • 事件声明: 发布者类必须声明一个订阅者类可以注册的事件成员。当类声明的事件为public时,称为 [b]发布了事件。
  • 事件注册: 订阅者必须注册事件才能在事件被触发时得到通知。这是将事件处理程序与事件相连的代码。
  • 触发事件的代码: 发布者类中“触发”事件并导致调用注册的所有事件处理程序的代码。

事件 event8776 作者:茎肛互撸娃 帖子ID:564

No.1 声明事件

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

class Incrementer{
    // 声明事件,事件名为 CountedADozen
    public event EventHandler CountedADozen;

    // 可以声明多个事件
    //public event EventHandler CountedADozen1,CountedADozen2;

    // 让事件变成静态
    //public static event EventHandler CountedADozen;
}

一个常见的误解是把事件视为类型,然而它不是。和方法、属性一样,事件是类或结构的成员,

  • 由于事件是成员:
    • 我们不能在一段可执行代码中声明事件;
    • 它必须声明在类或结构中,和其他成员一样。
  • 事件成员被隐式自动初始化为null。

事件声明需要委托类型的名称,我们可以声明一个委托类型或使用已有的委托类型。如果声明一个委托类型,它必须指定将被事件注册的方法的签名和返回类型。

No.2 订阅事件

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

incrementer.CountedADozen += IncrementDozensCount;    // 方法引用形式
incrementer.CountedADozen += ClassB.CounterHandlerB;    // 静态方法引用形式

mc.CountedADozen += new EventHandler(cc.CounterHandlerC);  // 委托形式

incrementer.CountedADozen += () => DozensCount++;    // Lambda 形式

incrementer.CountedADozen += delegate { DozensCount++; };   // 匿名方法形式

No.3 触发事件

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

如下代码出发了CountedADozen事件:

  • 在触发事件之前和null进行比较,从而查看事件是否包含事件处理程序。如果事件是null,则表示没有事件处理程序,不能执行。
  • 触发事件的语法和调用方法一样:
    • 使用事件名称,后面跟着参数列表(包含在圆括号中):
    • 参数列表必须与事件的委托类型相匹配。
if(CountedADozen !=null){     // 确认有方法可以执行
    CountedADozen(source, args);    // 触发事件
}

下面代码展示了整个程序,包含发布者类Incrementer和订阅者类Dozens
发布者:

delegate void EventHandler(object sender, EventArgs args);  // 声明委托

class Incrementer
{
    // 声明事件并发布
    public event EventHandler CountedADozen;

    public void DoCount()
    {
        for (int i = 1; i < 100; i++)
        {
            if (i % 12 == 0)
            {
                // 确认有方法可以执行
                if (CountedADozen != null)
                {
                    // 触发事件:每增加12个计数触发事件一次
                    CountedADozen(this, args);    
                }
            }
        }
    }
}

订阅者:

class Dozens
{
    public int DozensCount { get; private set; }

    public Dozens(Incrementer incrementer)
    {
        DozensCount = 0;
        incrementer.CountedADozen += IncrementDozensCount;    // 订阅事件
    }

    // 声明事件处理程序
    void IncrementDozensCount(object source, EventArgs args)
    {
        DozensCount++;
    }
}
class Program {
    void Main()
    {
        Incrementer incrementer = new Incrementer();
        Dozens dozensCounter = new Dozens(incrementer);
        incrementer.DoCount();
        Console.WriteLine(dozensCounter.DozensCount);
    }
}

扩展EventArgs来传递数据

移除事件处理程序

事件访问器

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

温馨提示

关于 注册码 问题

      由于近期经常大量注册机器人注册发送大量广告,本站开启免费入群领取注册码注册网站账号,注册码在群公告上贴着...

关于 注册码 问题

      由于近期经常大量注册机器人注册发送大量广告,本站开启免费入群领取注册码注册网站账号,注册码在群公告上贴着...

Archiver|手机版|小黑屋|DLSite

GMT+8, 2024-11-23 00:31

Powered by Discuz! X3.5 and PHP8

快速回复 返回顶部 返回列表