C# 事件
C# 事件
1.事件概述
事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。
C# 中使用事件机制实现线程间的通信。
事件在类中声明且生成,且通过使用同一个类或其他类中的委托与事件处理程序关联。包含事件的类用于发布事件。这被称为 发布器(publisher) 类。其他接受该事件的类被称为 订阅器(subscriber) 类。事件使用 发布-订阅(publisher-subscriber) 模型。
发布器(publisher) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。
订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。
2.使用事件有6个步骤:
事件处理程序:由订阅器注册到事件的方法,在发布器触发事件时执行。事件处理程序方法可以定义在事件所在的类或结构中,也可以定义在不同的类或结构中。
(1)声明委托:事件和事件处理程序必须有共同的签名和返回类型,它们通过委托类型进行描述。
(2)声明事件:发布器类必须声明一个订阅器类可以注册的事件成员。当声明的事件为public时,称为发布了事件。
(3)触发事件:发布器类中“触发”事件并导致调用注册的所有事件处理程序的代码。
(4)声明事件处理程序:事件处理程序必须具有与事件的委托相同的返回类型和签名。
(5)订阅事件并添加或删除事件处理程序:订阅者必须订阅事件才能在它被触发时得到通知。
(6)执行触发事件语句。
注意点:声明委托、声明事件、触发事件一般放在发布器类中;
声明事件处理程序一般放在订阅器类中;
订阅事件并添加或删除事件处理程序可以放在主函数中也可以放在订阅器类中
执行触发事件语句一般放在主函数中
3.声明事件:
1.声明事件代码
class Incrementer
{
//event 关键字
//EventHandler 是委托类型(委托类型是什么,这里就是什么),.Net框架提供事件使用的标准模式就是System命名空间声明的EventHandler委托类型(后面会提到)
//CountedADozen 事件名
public event EventHandler CountedADozen;
}
可以使用逗号分隔,同时声明多个事件
public event EventHandler myEvent1,myEvent2,myEvent3;
2.注意事项
(1)事件声明首先需要声明委托(如果使用默认的EventHandler委托类型,就不需要声明委托了),因为声明事件需要使用到委托类型,任何附加到事件的处理程序都必须与委托类型的签名和返回类型匹配。
(2)事件必须声明在一个类中,事件是类或结构的成员,事件成员被隐式自动化为null
(3)声明为public,这样其他类和结构可以在它上面注册事件处理程序。
(4)不能使用对象创建表达式(new 表达式)来创建它的对象。
4.订阅事件:
订阅者向事件添加事件处理程序,事件处理程序必须具有与事件的委托相同的返回类型和签名。
使用+=运算符来为事件增加事件处理程序。
事件处理程序的规范可以是以下任意一种:
实例方法的名称;静态方法的名称;委托形式;匿名方法;Lambda表达式
例如:
//incrementer这个对象是发布器类的对象
//CountedADozen 是事件名
//IncrementDozensCount 是实例方法
incrementer.CountedADozen+=IncrementDozensCount;//使用方法引用形式的实例方法
incrementer.CountedADozen+=ClassB.CounterHandlerB;//使用方法引用形式的静态方法
incrementer.CountedADozen+=new EventHandler (cc.CounterHandlerC);//委托形式
incrementer.CountedADozen+=()=>DozensCount++;//Lambda表达式
incrementer.CountedADozen+=delegate{DozensCount++;}//匿名方法
5.触发事件:
在触发事件之前和null进行比较,从而查看是否包含事件处理程序,如果事件是null,则表示没有,不能执行。
触发事件语法和调用方法一样:使用事件名称,后面跟的参数列表包含在圆括号中;参数列表必须与事件的委托类型相匹配
if(CountedADozen !=null)//确认有方法可以执行
{
//CountedADozen 事件名
CountedADozen (source,args);//触发事件
}
事件声明和触发事件的代码放在发布器类中:
6.完整代码示例:
完整代码展示如下:发布器类Incrementer和订阅器类Dozens,在构造函数中,Dozens类订阅事件,将Incrementer_CountedADozen作为事件处理程序;在Incrementer类的DoCount方法中,每增长5个数就触发CountedADozen事件。
发布器类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace demo8_0727
{//1.声明委托delegate void Handler();//发布器类internal class Incrementer{public event Handler CountedADozen;//2.声明事件public void DoCount(){for (int i = 0; i < 100; i++){if (i%5==0&&CountedADozen!=null) //判断条件{CountedADozen();//3.触发事件}} }}
}
订阅器类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace demo8_0727
{internal class Dozens{public int DozensCount { get; private set; }public Dozens(Incrementer incrementer ){DozensCount = 0;//5.订阅事件并添加事件处理程序incrementer.CountedADozen += Incrementer_CountedADozen;}private void Incrementer_CountedADozen()//4.声明事件处理程序{DozensCount++;}}
}
主函数类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace demo8_0727
{internal class Program{static void Main(string[] args){Incrementer incrementer = new Incrementer();Dozens dozens = new Dozens(incrementer);//6.执行触发事件语句incrementer.DoCount();Console.WriteLine("Number of dozens={0}",dozens.DozensCount);Console.ReadKey();}}
}
7.标准事件用法
.Net框架提供了事件使用的标准模式,.NET 类库中的所有事件均基于 EventHandler 委托,定义如下:
public delegate void EventHandler(object sender, EventArgs e);
//第一个参数用来保存触发事件的对象的引用
//第二个参数用来保存状态信息,指明什么类型适用于该应用程序:
无需声明该委托,因为它已在创建 C# 项目时包括的 System 命名空间中声明。
发布器类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace demo9_0727
{internal class Incrementer{//声明事件public event EventHandler CountedADozen;public void DoCount(){for (int i = 0; i < 100; i++){if (i%5==0&&CountedADozen!=null){//触发事件CountedADozen(this,null);}}}}
}
订阅器类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace demo9_0727
{internal class Dozens{public int DozensCount { get; private set; }public Dozens(Incrementer incrementer){DozensCount = 0;//订阅事件并添加事件处理程序incrementer.CountedADozen += Incrementer_CountedADozen;}//声明事件处理程序private void Incrementer_CountedADozen(object sender, EventArgs e){DozensCount++;}}
}
主函数类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace demo9_0727
{internal class Program{static void Main(string[] args){Incrementer incrementer = new Incrementer();Dozens dozens = new Dozens(incrementer);//执行触发事件语句incrementer.DoCount();Console.WriteLine("Number of dozens={0}",dozens.DozensCount);Console.ReadKey();}}
}
8.通过扩展EventArgs来传递数据
如果希望传递数据,必须声明一个派生自EventArgs 的类,使用合适的数据来保存需要的数据。
为了向自己的事件处理程序的第二个参数传入数据,需要声明一个派生自EventArgs的自定义类,它可以保存我们需要传入的数据。类的名称应该以EventArgs结尾。
要获得该类,需要使用泛型委托。(后续会提到)
代码演示:
自定义派生类IncrementerEventArgs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace demo5_0726
{//自定义派生类继承EventArgs类internal class IncrementerEventArgs: EventArgs{public int IterationCount { get; set; }//存储一个整数}
}
发布器类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace demo5_0726
{internal class Incrementer1{// 声明事件public event EventHandler<IncrementerEventArgs> CountedADozen;public void DoCount(){ IncrementerEventArgs args=new IncrementerEventArgs();for (int i = 0; i < 100; i++){if (i % 5 == 0 && CountedADozen != null){args.IterationCount = i;CountedADozen(this, args);//在触发事件时传递参数}}}}
}
订阅器类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace demo5_0726
{internal class Dozens1{//int _DozensCount;public int DozensCount{get;private set;}public Dozens1(Incrementer1 incrementer1){DozensCount = 0;incrementer1.CountedADozen += Incrementer1_CountedADozen;}private void Incrementer1_CountedADozen(object sender, IncrementerEventArgs e){Console.WriteLine("Incremented at iteration:{0} in {1}",e.IterationCount,sender.ToString());DozensCount++;}}
}
主类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace demo5_0726
{internal class Program{static void Main(string[] args){Incrementer1 incrementer1 = new Incrementer1();Dozens1 dozens1 = new Dozens1(incrementer1);incrementer1.DoCount();Console.WriteLine("Number of dozens={0}",dozens1.DozensCount);Console.ReadKey(); }}
}
相关文章:

C# 事件
C# 事件 1.事件概述 事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。 C# 中使用事件机制实现线程…...

网络:TCP/IP协议
1. OSI七层参考模型 应用层 表示层 会话层 传输层 网络层 数据链路层 物理层 2. TCP/IP模型 应用层 传输层 网络层 数据链路层 物理层 3. 各链路层对应的名称 应用层对应的是协议数据单元 传输层对应的是数据段 网络层对应的是数据包 链路层对应的是数据帧 物理层对应的是比特…...

在线阅读版:《2023中国软件供应链安全分析报告》全文
聚焦源代码安全,网罗国内外最新资讯! 专栏供应链安全 数字化时代,软件无处不在。软件如同社会中的“虚拟人”,已经成为支撑社会正常运转的最基本元素之一,软件的安全性问题也正在成为当今社会的根本性、基础性问题。 随…...

NLP_文本去重_附Python实现【MinHash和MinHashLSH】算法
NLP_文本去重_附Python实现【MinHash和MinHashLSH】算法 前言代码的实现【注释丰富】前言 大规模的文本去重是目前比较热门的一个技术,由于大模型的兴起,更多的高质量数据集也是大家迫切需要的。 关于如何进行文本去重? 直观的方法首先是利用Python正则表达式进行去重。 推…...

Excel Power View教程_编程入门自学教程_菜鸟教程-免费教程分享
教程简介 Excel Power View 是一种数据可视化技术,用于创建交互式图表、图形、地图和其他视觉效果,以便直观呈现数据。 Excel Power View中,可以快速创建各种可视化效果,从表格和矩阵到饼图、条形图和气泡图,以及多个…...

关于聊天功能,使用input发送消息,不能在input中显示图片解决办法
一般情况下,发送消息,上传文件、图片都是使用 input 来实现,但是产品的功能千变万化,现实中也会有不尽人意的时候 下方使用了element中的input 绑定Enter事件发送消息,但是有个功能点是 <el-input type"texta…...

SQL语句(三十二)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一、SQL语句类型 二、数据库操作 三、数据表操作 1. 数据类型 2. 查看 3. 创建 4. 删除 5. 更改 5.1 表 5.2 列 四、数据操作 4.1 增 4.2 删 4.3 改 4.4 查…...

ffmpeg-aresample_swr_opts的解析
ffmpeg option的解析 ffmpeg -y -i /home/hui/2ch-16k.wav -filter_size 16 -phase_shift 6 -ar 48000 out.wav其中-filter_size 16,-phase_shift 6是被当做option解析的,会进入opt_default函数,因为这两个参数是swresample的,所…...

PX4从放弃到精通(二十九):传感器冗余机制
文章目录 前言一、parametersUpdate二、imuPoll三、 put四、 confidence五、 get_best 前言 PX4 1.13.2 一个人可以走的更快,一群人才能走的更远,可加文章底部微信名片 代码的位置如下 PX4冗余机制主要通过传感读数错误计数和传感器的优先级进行选优 …...

vue 设置数组
手写获取数据 <el-form-item label"缴纳方"><el-select v-model"form.invoiceCategoryName" placeholder"请选择缴纳方"><el-optionv-for"item in kplmList":key"item.value":label"item.label":v…...

9.NIO非阻塞式网络通信入门
highlight: arduino-light Selector 示意图和特点说明 一个 I/O 线程可以并发处理 N 个客户端连接和读写操作,这从根本上解决了传统同步阻塞 I/O 一连接一线程模型。架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。 服务端流程 1、当客户端连接服务端时&…...

QT基于TCP协议实现数据传输以及波形绘制
这个玩意我做了两个,一个是安卓app,一个是Windows程序。代码并非全部都是由我从无到有实现,只是实现了我想要的功能。多亏了巨人的肩膀,开源万岁!!! 我把程序放到GitHub上,需要的可…...

苹果safari浏览器播放不了video标签视频
今天遇到了个神奇的问题,视频文件在pc端和安卓手机上播放都没问题,但是在ios上就是播放不了,大概代码如下: 前端代码: <video id"video" width"350" height"500" controls><s…...

【粒子群算法和蝴蝶算法组合】粒子群混沌混合蝴蝶优化算法研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

Java设计模式之单例模式详解(懒汉式和饿汉式)
在开发工作中,有些类只需要存在一个实例,这时就可以使用单例模式。Java中的单例模式是一种常见的设计模式,它确保一个类只有一个实例,并提供全局访问点。下面来介绍一下两种常见的单例模式:懒汉式和饿汉式。 一、懒汉式…...

软件测试基本知识
安全测试 安全防护策略?(漏洞扫描、入侵检查、安全日志、隔离防护) 安全日志:用于记录非法用户的登录名称、操作时间及内容等信息,以便发现问题并提出解决措施;安全日志仅记录相关信息,不对非…...

Vue项目中强制刷新页面的方法
我们在动态切换组件的过程中,导航栏和底栏不动,动态切换中间区域的情况,在首页可以进行跳转任意组件,在组件与组件之间不能相互跳转,路由发生了变化,但是页面未改变,这时我们就需要强制刷新页面…...

文件按关键字分组-切割-染色-写入excel
1. 背景 针对下面的文件data.csv,首先根据fid进行排序,然后分组,使相同fid的记录放到同一个excel文件中,并对每列重复的数据元素染上红色。 fid,user_id -1000078398032092029,230410010036537520 -1000078398032092029,23042301…...

爬虫的基本原理:爬虫概述及爬取过程
前言 随着互联网的不断发展和普及,我们的生活越来越离不开网络。而网络世界中有着海量的信息和数据,这些信息和数据对于我们的工作和生活都有很大的帮助。但是,如何高效地获取这些数据呢?这时候,爬虫这个工具就派上用…...

cocos2D插件转3D插件
cocos2D插件转3D插件 use strict;/*** 3d插件api映射,兼容2d插件* */let fs require("fs");let path require("path");let baseDir ;const prsPath (Editor.Project && Editor.Project.path ? Editor.Project.path : Editor.remote.projectP…...

[Angular] 主从表结构,从表记录在主表固定栏位上呈现
Background 主从表结构,有时为了方便数据呈现,在UI上不显示从表资料,那么需要动态把从表的资料加载到主表的固定栏位上。 例如:主表是人员信息,从表是银行卡信息,一个人在同一家银行可能有多张银行卡&…...

Kotlin Multiplatform 创建多平台分发库
目标:通过本教程学习如何使用 Kotlin Multiplatform Library 创建多平台分发库(iOS,安卓)。 创建一个项目 1、本教程使用的是Android Studio创建 2、选择 新建工程,选择 Kotlin Multiplatform Library 3、点击next 输入需要创建的项目名称以…...

[SQL挖掘机] - union/union all 使用注意事项
因为当使用union和union all操作符时,有一些注意事项需要考虑: 1. 列数和数据类型匹配: 要使用union或union all合并结果集,两个或多个查询的 select 语句必须返回相同数量和类型的列。确保每个查询返回相同的列数,并…...

php 单例模式
1,单例模式,属于创建设计模式,简单来说就是一个类只能有一个实例化对象,并提供一个当前类的全局唯一可访问入口; 2,例子 <?phpclass Singleton {private static $instance null;// 禁止被实例化priva…...

【数据结构】实验二:顺序表
实验二 顺序表 一、实验目的与要求 1)熟悉顺序表的类型定义; 2)熟悉顺序表的基本操作; 3)灵活应用顺序表解决具体应用问题。 二、实验内容 1)在一个整数序列a1,a2,…,an中,若存在一个数&…...

【高级数据结构】线段树
目录 最大数(单点修改,区间查询) 线段树1(区间修改,区间查询) 最大数(单点修改,区间查询) 洛谷:最大数https://www.luogu.com.cn/problem/P1198 题目描述 …...

qt简易闹钟
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);ui->stopBtn->setDisabled(true);this->setFixedSize(this->size()); //设置固定大小this->s…...

python和c加加有什么区别,c和c++和python先学哪个
本篇文章给大家谈谈c加加编程和python编程有什么区别,以及python和c加加有什么区别,希望对各位有所帮助,不要忘了收藏本站喔。 1、python和c学哪个好 学C好。 C通常比Python更快,因为C是一种编译型语言,而Python则是…...

Visual Studio 2022 cmake配置opencv开发环境
1. 环境与说明 这里我用的是 widnows 10 64位,Visual Studio 用的 Visual Studio Community 2022 (社区版) 对于Android开发工程师来说,为什么要使用Visual Studio 呢 ? 因为在Visual Studio中开发调试OpenCV方便,可以开发调试好后…...

C++ GDAL找出多时相遥感影像缺失的日期并自动生成新的全零图像作为替补
本文介绍基于C 语言的GDAL库,基于一个存储大量遥感影像的文件夹,依据每一景遥感影像的文件名中表示日期的那个字段,找出这些遥感影像中缺失的成像日期,并新生成多个像元值全部为0的栅格文件,作为这些缺失日期当日的遥感…...