C#学习笔记:使用事件

事件与委托有着密切的关系,因为事件自身就是委托类型。由于委托可以绑定和调用多个方法,所以会为时间的处理带来方便。类型只需要对外公开事件,就可以与外部的其他地方进行关联,从而实现事件订阅。
首先,我们先介绍一下事件订阅的概念。在实际生活中,假设我们订阅了某新闻平台的邮件通知服务,只要有新闻更新,服务提供方就要发送通知邮件。因此,事件订阅也是如此,前文分析过选用委托作为事件的类型的理由,就是委托可以与其他方法关联,当A方法与X事件进行了关联,只要X事件发生(相当于新闻内容有更新),就会调用作为事件的委托(相当于服务提供方发送通知邮件),因为A方法与X事件关联,所以A方法也会被调用,于是代码就能够响应事件。
响应事件就像战士听到了冲锋号响了,知道该冲锋了。战士的反应就相当于处理事件的方法,而冲锋号响起就相当于表示事件的委托被调用。
事件是委托类型,因此,需要在类中声明事件,首先要定义用来作为事件封装类型的委托,然后在类中用event关键字来声明事件。通常会在类中声明一个受保护的方法,习惯上命名为On<事件名>,然后在这个方法中调用事件。

  1. 首先新建一个项目,项目生成的命名空间中定义一个委托类型,作为响应按下空格键这一事件的封装类型。

      public delegate void SpaceKeyPressEventHandler();
    
  2. 定义一个MyApp类,代码如下:

public class MyApp
{
///
/// 声明事件
///
public event SpaceKeyPressEventHandler SpaceKeyPressed;
//声明了一个系统自带的事件,即当按下空格键的事件

    /// <summary>
    /// 通过该方法引发事件
    /// </summary>
    protected virtual void OnSpaceKeyPressed()
    {
        //触发SpaceKeyPressed事件时必须判断这个事件是不是与其他方法关联了
        //如果这个委托类型的事件没有关联其他的方法,就不触发事件
        if (this.SpaceKeyPressed != null)
        {
            SpaceKeyPressed();
        }
    }

    public void StartRun()
    {
        while (true)
        {
            ConsoleKeyInfo keyinfo = Console.ReadKey();
            if (keyinfo.Key == ConsoleKey.Spacebar)
            {
                //引发事件
                OnSpaceKeyPressed();
            }
            if (keyinfo.Key == ConsoleKey.Escape)
            {
                //跳出循环
                break;
            }

        }
        
    }

}

在这个类中,用刚才定义的委托声明了一个SpaceKeyPressed事件。然后封装自爱OnSpaceKeyPressed方法中调用,因为这个表是事件的委托可能是空的,调用方法可能不响应事件处理,即没有与之关联的方法。所以,我们在调用前必须先判断一下表示事件的委托实例是否为null.

还有一种更简单的写法,不需要if语句进行盘多,而是直接调用事件,但在事件名称后面加上一个“?”(英文的问号),代码如下

    protected virtual void OnSpaceKeyPressed()
    {
          this.SpaceKeyPressed?.Invoke();
    }

这样写之后,程序就会自动判断SpaceKeyPressed是否为null,如果为null,这行代码就不会被执行,直接跳过。如果不为null就执行这行代码。
在StartRun方法中启动了一个无限循环,也就先不要用下面这段代码来跳出循环。

            if (keyinfo.Key == ConsoleKey.Escape)
            {
                //跳出循环
                break;
            }

这样,当按下的是Esc时,用break语句直接退出循环。

如果按下的键是空格键,就调用OnSpaceKeyPressed方法,这样一来,事件就被触发了。而代码在使用MyApp类时,先新建一个实例,然后通过+=运算符使SpaceKeyPressed事件与相关的方法关联起来。在VS中,只要在事件名后面输入+=,再按一下Tab键,这时候会生成一个默认的方法名(变量名_方法名),因为这个方法名处于选定状态,我们可以对其进行重命名(如图所示),如果不需要重命名,就再按一下Tab键,就会生成事件处理方法。

在这里插入图片描述
我们将方法中的处理代码修改如下:
在这里插入图片描述
运行程序后,只要按下空格键,屏幕上就会输出提示信息。
在引发事件时,很多时候都会考虑一下传递一些数据。比如一个捕捉鼠标操作的事件,光是引发事件是不够的,事件的处理程序还需要知道用户进行了哪些鼠标操作,是否移动了鼠标指针,或者是按下了鼠标上的某个键;如果是按下了鼠标上的键,是左键还是右键…可见,在引发事件的时候,还有必要传递一些描述性的信息,以便事件处理代码能够获得更详细的数据。
通常,作为事件委托,有两个参数,一个是Object类型,表示引发事件的对象,即是谁引发了事件,多数情况下,在调用事件时是把类的当前实例引用(this)传递过去。另一个参数是从System.EventArgs派生的类的实例。这是一个标准的事件处理程序的签名,为了规范事件的处理,.Net类库已经定义好了一个System.EventHandler委托,用于声明事件。它的原型如下:

public delegate void EventHandler(Object sender, System.EventArgs e);

引发事件的对象实例将传递给sender参数,参与事件的相关数据则传递给e参数,如果不需要传递过多的数据,可以通过System。EventArgs.Empty静态成员返回一个EventArgs对象来传递。
但是,由于不同的事件要传递的参数不同,更多时候是从EventArgs类派生的子类的实例,显然一个EventHandler委托是不能满足各种情况的。如果针对不同类型的事件也定义一个对应的委托,数量一旦多起来,既混乱又不好管理。为了解决这个问题,.NET类库又提供了一个带有泛型参数的事件处理委托。原型如下:

  public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

TEventArgs是一个泛型参数,TEventArgs应该是System.EventArgs类或者System.EventArgs类的派生类型。

有了EventHandler委托,开发者就可以应对各种各样的事件了。因为对于不同的事件,第一个参数是不变的,只是第二个参数的类型有差异而已。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值