当前位置: 首页 > article >正文

C#中的事件

事件概述

很多程序会有一个共同的需求,即当一个特定的程序事件发生时,程序的其他部分可以得到该事件已经发生的通知。事件基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,点击按钮,显示消息框。事件是一个发布-订阅模型,发布者定义了一系列程序的其他部分可能感兴趣的事件,其他类就可以注册,以便在这些事件发生时收到发布者的通知。
关于事件的重要概念:
发布器: 一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。包含事件的类用于发布事件。这被称为 发布器(publisher) 类。
订阅器: 是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。接受该事件的类被称为 订阅器(subscriber) 类。
事件处理程序: 由订阅者注册到事件的方法,可以定义在发布者类或结构,也定义在不同的类或结构。

事件声明

发布者必须提供事件对象,事件声明的语法如下代码所示:

        public delegate void StudentHanler(object o, EventArgs e);
        public event StudentHanler Study;

event关键字+委托类型+事件名称
事件是成员,不是类型,因此它必须声明在类和结构中,事件成员被隐式自动初始化时为null。

订阅事件

订阅者向事件添加事件处理程序。使用+=运算符为事件添加处理程序,移除事件处理程序则使用-=,事件处理程序位于该运算符的右边。事件处理程序的规范可以是以下任意一种:

  • 实例方法的名称
  • 静态方法的名称
  • 匿名方法
  • Lambda表达式

笔者用的习惯是匿名方法和lambda表达式,如下所示

stu.Study += delegate (object o, StudyEventArgs e) { Console.WriteLine($"{e.StuName}学习中2"); };//匿名方法形式

 stu.Study += (o, e) =>
 {
     Console.WriteLine($"{e.StuName}学习中1");
 };//订阅事件 lambda表达式形式

标准事件的用法

关于事件的使用,.Net框架提供了一个标准模式,该标准模型的基础就是System名称空间声明的EventHandler委托类型,EventHandler委托类型声明如下代码所示

public delegate void EventHandler(object o,EventArgs e)
  • 第一个参数用于保存触发事件对象的引用。因为它是object类型,所有可以匹配任何类型的实例。
  • 第二个参数用来保存状态信息,指明什么类型使用于该程序,EventArgs 不能传递任何数据,它用于不需要传递数据的事件处理程序,如果想要传递数据,则需要声明一个派生于EventArgs 的类,并使用合适的字段来保存需要传递的数据。
  • 返回类型是void

通过拓展EventArgs来传递数据

为了向事件处理程序添加第二个参数来传递数据,可以需要声明一个派生EventArgs的自定义类,同时遵循标准惯例,类的名称应该以EventArgs结尾。

    public class StudyEventArgs:EventArgs//自定义类(后缀为EventArgs):基类
    {
    	//用于保存数据
		public String stuName;
		public String StuName
		{
			get { return stuName; }
			set { stuName = value; }
		}
		//构造函数初始化
		public StudyEventArgs(string name)
		{
			StuName = name;

        }

触发事件

在触发事件可以和null比较,如果事件是null,则表示没有事件处理程序,不能执行,事件触发的语法和调用方法一样:

  • 使用事件名称,后面跟着参数列表
  • 参数列表必须与事件的委托类型相匹配

常用到的触发方式有如下3种

Study?.Invoke(source, args);//触发事件 使用空运算符判断事件是否为空和使用Invoke进行触发Study事件
if(Study !=null){Study(source, args)}

另外一种是在发布者类定义一个属性,如果属性的值设置则触发,可参考应用案例
应用
笔者定义了两个类,分别为Student类和StudyEventArgs类,Student类用于声明事件,和Name属性,触发条件和事件触发(事件触发必须写在发布者类)。StudyEventArgs类用来拓展事件参数,用于传递参数
代码如下:
Student类

    public class Student
    {
        public delegate void StudentHanler(object o, StudyEventArgs e);
        public event StudentHanler Study;
        
        public string Name { get; set; }

        private bool isStart;

        public bool IsStart
        {
            get { return isStart; }
            set { isStart = value; Start(); }
        }

        public void Start()
        {
            if (IsStart)
            {
                Study?.Invoke(this, new StudyEventArgs(Name));//触发事件
            }
            
        }

StudyEventArgs类

    public class Program
    {
        static void Main(string[] args)
        {

            Student stu = new Student() { Name = "小明" };
            
            stu.Study += (o, e) =>
            {
                Console.WriteLine($"{e.StuName}学习中1");
            };//订阅事件 lambda表达式形式订阅
            stu.Study += delegate (object o, StudyEventArgs e) { Console.WriteLine($"{e.StuName}学习中2"); };//匿名方法形式订阅
            stu.Study += new StudentHanler(study);//委托的形式 订阅
           
            stu.IsStart = true;

        }
        static void study(object o, StudyEventArgs e) 
        {
            Console.WriteLine($"{e.StuName}学习中3");
        }
    }

运行结果
在这里插入图片描述
总结:在该程序中发布者为Student类,订阅者为为Program类,使用委托形式订阅事件时study方法为事件处理程序,在=>右边。通过设置IsStart属性来控制事件的调用。其中在事件触发时Study?.Invoke(this, new StudyEventArgs(Name)),this指向该Student的实例,即对象小明

笔者不才,如以上内容理解错误,请大佬们指正!


http://www.kler.cn/a/369194.html

相关文章:

  • 如何将markdown文件转换为pdf
  • docker安装、设置非sudo执行、卸载
  • Video-XL:面向小时级视频理解的超长视觉语言模型
  • Java题集练习4
  • 【状态机DP】力扣1186. 删除一次得到子数组最大和
  • 《手写Spring渐进式源码实践》实践笔记(第十一章 AOP-基于JDK、Cglib实现对象动态代理)
  • 029_Common_Plots_Matlab常见二维绘图
  • 【阅读笔记】Instruction-based Hypergraph Pretraining
  • PHP如何实现字符串翻转
  • 【实战案例】Django框架表单处理及数据库交互
  • 【YOLOv11[基础]】实例分割 + 跟踪
  • 二叉树习题其六【力扣】【算法学习day.13】
  • 基于KV260的基础视频链路通路(MIPI+Demosaic+VDMA)
  • Page Cache(页缓存)的大小如何确定
  • Win11安装基于WSL2的Ubuntu
  • 大型语言模型与人类价值观对齐:去中心化开放数据获取平台
  • NVR管理平台EasyNVR多个NVR同时管理汇聚方案
  • STM32 RTC时间无法设置和读取
  • 【K8s】Kubernetes 证书管理工具 Cert-Manager
  • 【mysql 进阶】2-1. MySQL 服务器介绍
  • 小米面试题:多级缓存一致性问题怎么解决
  • UDP 实现的 Echo Server 和 Echo Client 回显程序
  • 雷池社区版OPEN API使用教程
  • 模拟 DDoS 攻击与防御实验
  • 链表学习的疑惑
  • etcd之etcd分布式锁及事务(四)