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

C#中的事件(委托的发布和订阅、事件的发布和订阅、EventHandler类、Windows事件)

目录

一、委托的发布和订阅

1.订阅操作符号“+="和取消订阅操作符号“-=”

2.示例源码

二、事件的发布和订阅

三、EventHandler类

四、Windows事件


        C#中的事件是指某个类的对象在运行过程中遇到的一些特定事情,而这些特定的事情有必要通知给这个对象的使用者。当发生与某个对象相关的事件时,类会使用事件将这一对象通知给用户,这种通知即称为“引发事件”。引发事件的对象称为事件的源或发送者。

一、委托的发布和订阅

        由于委托能够引用方法,而且能够链接和删除其他委托对象,因而就能够通过委托来实现事件的“发布和订阅”。

  通过委托来实现事件处理的过程,通常需要以下4个步骤:
• 定义委托类型,并在发布者类中定义一个该类型的公有成员。
• 在订阅者类中定义委托处理方法。
• 订阅者对象将其事件处理方法链接到发布者对象的委托成员(一个委托类型的引用)上。
• 发布者对象在特定的情况下“激发”委托操作,从而自动调用订阅者对象的委托处理方法。

1.订阅操作符号“+="和取消订阅操作符号“-=”

         “+=”在这里不是逻辑运算符,而是用于指定响应事件时要调用的方法。这类方法称为事件处理程序,叫 注册/订阅事件,用在操作类名后。

//订阅符号+=
public static void SubscribeToRing(SchoolRing schoolRing)  
{schoolRing.OnBellSound += SchoolJow;
}

        与之相反功能的“-=”就是取消订阅、退订操作符。

//取消订阅操作符“-=”
public static void CancelSubscribe(SchoolRing schoolRing)  
{schoolRing.OnBellSound -= SchoolJow;
}

2.示例源码

// 委托的发布和订阅事件namespace Demo
{class Program{static void Main(string[] args){SchoolRing sr = new();                          //创建学校铃声类的对象Students.SubscribeToRing(sr);                   //订阅铃声Console.Write("请输入打铃参数(1:表示打上课铃;2:表示打下课铃):");sr.Jow(Convert.ToInt32(Console.ReadLine()));    //打铃动作Console.ReadLine();}}public delegate void RingEvent(int ringKind);           //声明一个委托类型/// <summary>/// 定义铃声类SchoolRing/// 类中发布一个委托,定义函数Jow/// </summary>public class SchoolRing{public RingEvent? OnBellSound;            //委托发布,就好像定义一个实例对象public void Jow(int ringKind)             //定义一个公有成员Jow(),打铃{if (ringKind == 1 || ringKind == 2){Console.Write(ringKind == 1 ? "上课铃声响了," : "下课铃声响了,");if (OnBellSound != null)         //不等于空,说明它已经订阅了具体的方法(即它已经引用了具体的方法){OnBellSound!(ringKind);      //回调OnBellSound委托所订阅(或引用)的具体方法}}else{Console.WriteLine("这个铃声参数不正确!");}}}/// <summary>/// 定义学生类Students/// 类中定义三个函数/// </summary>public class Students{public static void SubscribeToRing(SchoolRing schoolRing)  //学生们订阅铃声这个委托事件{schoolRing.OnBellSound += SchoolJow;}public static void SchoolJow(int ringKind){if (ringKind == 2)             //打了下课铃{Console.WriteLine("同学们开始课间休息!");}else if (ringKind == 1)        //打了上课铃{Console.WriteLine("同学们开始认真学习!");}}public static void CancelSubscribe(SchoolRing schoolRing)  //取消订阅铃声动作{schoolRing.OnBellSound -= SchoolJow;}}
}

二、事件的发布和订阅

        事件是一种特殊的类型,发布者在发布一个事件之后,订阅者对它只能进行自身的订阅或取消,而不能干涉其他订阅者。
事件是类的一种特殊成员:即使是公有事件,除了其所属类型之外,其他类型只能对其进行订阅或取消,别的任何操作都是不允许的,因此事件具有特殊的封装性。和一般委托成员不同,某个类型的事件只能由自身触发。

        使用事件的目的是:解决安全隐患和不能干涉其他订阅者。事件的使用方法:C#提供了专门的事件处理机制,以保证事件订阅的可靠性,其做法是在发布委托的定义中加上event关键字,其他代码不变。

//事件的使用方法
public event RingEvent OnBellSound;    //事件发布
//不安全的事件订阅,当不使用event关键字时,系统会会忽视威胁的存在
//当使用event关键字修饰时,系统会报错
schoolRing.OnBellSound = SchoolJow;   //系统会报错的,应使用+=
schoolRing.OnBellSound = null;        //系统会报错的,禁止指向null
schoolRing.OnBellSound2 = SchoolJow;  //系统会报错的,事件只能由自身触发

三、EventHandler类

        在事件发布和订阅的过程中,定义事件的类型(即委托类型)是一件重复性的工作,为此,.NET类库中定义了一个EventHandler委托类型,并建议尽量使用该类型作为事件的委托类型。该委托类型的定义为:

public delegate void EventHandler(object sender,EventArgs e);

        其中,

        object类型的参数sender表示引发事件的对象,由于事件成员只能由类型本身(即事件的发布者)触发,因此在触发时传递给该参数的值通常为this。例如,可将SchoolRing类的OnBellSound事件定义为EventHandler委托类型,那么触发该事件的代码就是“OnBellSound(this,null);”

        EventHandler委托的第二个参数e表示事件中包含的数据。如果发布者还要向订阅者传递额外的事件数据,那么就需要定义EventArgs类型的派生类。

// EventHandler类
namespace _09_1
{class Program{/// <summary>/// 操作流程:创建发布者实例→订阅该实例→发布者开始发布/// </summary>static void Main(string[] args){SchoolRing sr = new();                                     //创建学校铃声类的对象Students.SubscribeToRing(sr);                          //订阅铃声Console.Write("请输入打铃参数(1:表示打上课铃;2:表示打下课铃):");sr.Jow(Convert.ToInt32(Console.ReadLine()));    //发布者触发打铃动作,事件只能由发布者触发Console.ReadLine();}}public delegate void RingEvent(int ringKind);           //声明一个委托类型/// <summary>/// 发布者/// 校铃种类及对应的处理方法/// 定义铃声类SchoolRing,类中发布一个委托,定义函数Jow方法/// </summary>public class SchoolRing{public event EventHandler? OnBellSound;   //委托发布,就好像定义一个实例对象public void Jow(int ringKind)                      //定义一个公有成员Jow(),打铃方法{if (ringKind == 1 || ringKind == 2){Console.Write(ringKind == 1 ? "上课铃声响了," : "下课铃声响了,");if (OnBellSound != null)                    //不等于空,说明它已经订阅了具体的方法(即它已经引用了具体的方法){                                                     //为了安全,事件成员只能由类型本身触发(this),OnBellSound!(this, new Students.RingEventArgs(ringKind));}}else{Console.WriteLine("这个铃声参数不正确!");}}}/// <summary>/// 订阅者/// 定义学生类Students/// 类中定义三个函数:订阅、订阅方法、取消订阅/// </summary>public class Students{/// <summary>/// 订阅/// </summary>public static void SubscribeToRing(SchoolRing schoolRing)  //学生们订阅铃声这个委托事件{schoolRing.OnBellSound += SchoolJow;}/// <summary>/// EventHandler委托的第二个参数e表示事件中包含的数据。/// </summary>/// <param name="sender">/// 事件的订阅者可以通过sender参数来了解是哪个对象触发的事件(这里当然是事件的发布者),/// 不过在访问对象时通常要进行强制类型转换/// </param>/// <param name="e"></param>public static void SchoolJow(object? sender, EventArgs e){if (((RingEventArgs)e).RingKind == 2)             //下课铃,e强制转化内RingEventArgs类型{Console.WriteLine("同学们开始课间休息!");}else if (((RingEventArgs)e).RingKind == 1)     //上课铃,e强制转化内RingEventArgs类型{Console.WriteLine("同学们开始认真学习!");}}/// <summary>/// 取消订阅/// </summary>/// <param name="schoolRing"></param>public static void CancelSubscribe(SchoolRing schoolRing){schoolRing.OnBellSound -= SchoolJow;}/// <summary>/// EventArgs类型的派生类/// 如果发布者还要向订阅者传递额外的事件数据,那么就需要定义EventArgs类型的派生类。/// 例如,由于需要把打铃参数(1或2)传入事件中,则可以定义如下的RingEventArgs类:/// </summary>/// <param name="ringKind">/// 铃声参数/// </param>public class RingEventArgs(int ringKind) : EventArgs{//描述铃声种类的字段private readonly int ringKind = ringKind;//获取打铃参数public int RingKind         {get { return ringKind; }}}}
}

四、Windows事件

        事件在Windows这样的图形界面程序中有着极其广泛的应用,事件响应是程序与用户交互的基础。用户的绝大多数操作,都可以触发相关的控件事件。关于此类事件,详见作者发布的有关Windows窗体应用的文章,此处省略十万字。

相关文章:

C#中的事件(委托的发布和订阅、事件的发布和订阅、EventHandler类、Windows事件)

目录 一、委托的发布和订阅 1.订阅操作符号“"和取消订阅操作符号“-” 2.示例源码 二、事件的发布和订阅 三、EventHandler类 四、Windows事件 C#中的事件是指某个类的对象在运行过程中遇到的一些特定事情&#xff0c;而这些特定的事情有必要通知给这个对象的使用者…...

世微 舞台灯深度调光 大功率 dc-dc降压恒流驱动IC APS54083

产品描述 APS54083 是一款 PWM 工作模式,高效率、外围简单、外置功率 MOS 管&#xff0c;适用于 5-220V 输入高精度降压 LED 恒流驱动芯片。输出最大功率150W最大电流 6A。APS54083 可实现线性调光和 PWM 调光&#xff0c;线性调光脚有效电压范围 0.5-2.5V.PWM 调光频率范围 10…...

【nlp】3.3 Transformer论文复现:2. 编码器部分(掩码张量、多头注意力机制、前馈全连接层)

Transformer论文复现:2. 编码器部分(掩码张量、多头注意力机制、前馈全连接层) 2 编码器复现2.1 编码器介绍2.2 掩码张量2.2.1 掩码张量介绍2.2.2 掩码张量的作用2.2.3 生成掩码张量的代码实现2.2.4 掩码张量的可视化2.2.5 掩码张量总结2.3 注意力机制2.3.1 注意力计算规则的…...

vue3中shallowReactive与shallowRef

shallowReactive与shallowRef shallowReactive: 只处理了对象内最外层属性的响应式(也就是浅响应式) shallowRef: 只处理了value的响应式, 不进行对象的reactive处理 总结: reactive与ref实现的是深度响应式, 而shallowReactive与shallowRef是浅响应式。 什么时候用浅响应…...

蓝桥杯-动态规划-子数组问题

目录 一、乘积最大数组 二、乘积为正数的最长子数组长度 三、等差数列划分 四、最长湍流子数组 心得&#xff1a; 最重要的还是状态表示&#xff0c;我们需要根据题的意思&#xff0c;来分析出不同的题&#xff0c;不同的情况&#xff0c;来分析需要多少个状态 一、乘积最…...

CDA一级备考思维导图

CDA一级备考思维导图 第一章 数据分析概述与职业操守1、数据分析概念、方法论、角色2、数据分析师职业道德与行为准则3、大数据立法、安全、隐私 CDA一级复习备考资料共计七个章节&#xff0c;如需资料&#xff0c;请留言&#xff0c;概览如下图&#xff1a; 第一章 数据分析…...

【傻瓜级JS-DLL-WINCC-PLC交互】1.C#用windows窗体控件创建.net控件

思路 JS-DLL-WINCC-PLC之间进行交互&#xff0c;思路&#xff0c;先用Visual Studio创建一个C#的DLL控件&#xff0c;然后这个控件里面嵌入浏览器组件&#xff0c;实现JS与DLL通信&#xff0c;然后DLL放入到WINCC里面的图形编辑器中&#xff0c;实现DLL与WINCC的通信。然后PLC与…...

Unity中Shader的BRDF解析(一)

文章目录 前言现在我们主要来看Standard的 漫反射 和 镜面反射一、PBS的核心计算BRDF二、Standard的镜面高光颜色三、具体的BRDF计算对于BRDF的具体计算&#xff0c;在下篇文章中&#xff0c;继续解析 四、最终代码.cginc文件Shader文件 前言 在上篇文章中&#xff0c;我们解析…...

《软件工程原理与实践》复习总结与习题——软件工程概述

软件 什么是软件&#xff1f; 程序数据配套文档 软件危机 概念 计算机软件的开发和维护过程中所遇到的一系列严重问题 表现 20世纪60年代中后期&#xff0c;大容量、高速度计算机的出现&#xff0c;使计算机应用范围增大&#xff0c;软件开发需求急剧增长 软件工程 背景 德国…...

acwing算法基础之动态规划--线性DP和区间DP

目录 1 基础知识2 模板3 工程化 1 基础知识 线性DP&#xff1a;状态转移表达式存在明显的线性关系。 区间DP&#xff1a;与顺序有关&#xff0c;状态与区间有关。 2 模板 3 工程化 题目1&#xff1a;数字三角形。 解题思路&#xff1a;直接DP即可&#xff0c;f[i][j]可以来…...

力扣 622.设计循环队列

目录 1.解题思路2.代码实现 1.解题思路 首先&#xff0c;该题是设计循环队列&#xff0c;因此我们有两种实现方法&#xff0c;即数组和链表&#xff0c;但具体考虑后&#xff0c;发现数组实现要更容易一些&#xff0c;因此使用数组实现&#xff0c;因此我们要给出头和尾变量&a…...

初识Linux(2).妈妈再也不用担心我Linux找不到门了。

文章目录 前言 1.man指令&#xff08;重要&#xff09;&#xff1a;例如&#xff1a; 2.cp指令&#xff08;重要&#xff09;&#xff1a;例如&#xff1a;把123.txt复制到a目录中类似window如下操作&#xff1a; 3.mv例如&#xff1a;类似window如下操作&#xff1a; 4.nano例…...

房屋租赁出售经纪人入驻小程序平台

一款专为房屋中介开发的小程序平台&#xff0c;支持独立部署&#xff0c;源码交付&#xff0c;数据安全无忧。 核心功能&#xff1a;房屋出租、经纪人独立后台、分佣后台、楼盘展示、房型展示、在线咨询、地址位置配套设施展示。 程序已被很多房屋交易中介体验使用过&#x…...

【计算方法与科学建模】矩阵特征值与特征向量的计算(五):乘幂法的加速(带有原点移位的乘幂法)

文章目录 一、Jacobi 旋转法二、Jacobi 过关法三、Householder 方法四、乘幂法四、乘幂法的加速 矩阵的特征值&#xff08;eigenvalue&#xff09;和特征向量&#xff08;eigenvector&#xff09;在很多应用中都具有重要的数学和物理意义。 本文将详细介绍乘幂法的基本原理和步…...

2023年【起重机械指挥】考试题库及起重机械指挥考试资料

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年【起重机械指挥】考试题库及起重机械指挥考试资料&#xff0c;包含起重机械指挥考试题库答案和解析及起重机械指挥考试资料练习。安全生产模拟考试一点通结合国家起重机械指挥考试最新大纲及起重机械指挥考试真…...

GoLang语言范围(Range)

目录 一、在数组、切片上使用‘range’ 二、在映射上使用range 三、在通道上使用range Go语言中的range关键字用于迭代数组&#xff08;数组、切片、字符串&#xff09;、映射&#xff08;map&#xff09;、通道&#xff08;channel&#xff09;或者在 for 循环中迭代每一个…...

汽车电子 -- 车载ADAS之FCW(前方碰撞预警)

相关法规文件: FCW: GB∕T 33577-2017 智能运输系统 车辆前向碰撞预警系统 性能要求和测试规程 一、前方碰撞预警 FCW&#xff08; Forward Collision Warning&#xff09; 参看&#xff1a;法规标准-GB/T 33577标准解读(2017版) 1、状态机 系统关闭 当车辆前向碰撞预警系…...

爬虫系统Docker和Kubernetes部署运维最佳实践

在构建和管理爬虫系统时&#xff0c;使用Docker和Kubernetes可以带来诸多好处&#xff0c;如方便的部署、弹性伸缩和高可靠性。然而&#xff0c;正确的部署和运维实践对于确保系统稳定运行至关重要。在本文中&#xff0c;我将分享爬虫系统在Docker和Kubernetes上的最佳部署和运…...

音视频5、libavformat-1

libavformat库,是FFmpeg中用于处理各种媒体容器格式(media container format)的库。它的两个最主要的功能是 : demuxing:解封装,将一个媒体文件分割为多个多媒体流 muxing:封装,将多个多媒体数据流写入到指定媒体容器格式的文件中 这两个过程所做的…...

【数据结构复习之路】树和二叉树(严蔚敏版)万字详解主打基础

专栏&#xff1a;数据结构复习之路 复习完上面四章【线性表】【栈和队列】【串】【数组和广义表】&#xff0c;我们接着复习 树和二叉树&#xff0c;这篇文章我写的非常详细且通俗易懂&#xff0c;看完保证会带给你不一样的收获。如果对你有帮助&#xff0c;看在我这么辛苦整理…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

Linux系统部署KES

1、安装准备 1.版本说明V008R006C009B0014 V008&#xff1a;是version产品的大版本。 R006&#xff1a;是release产品特性版本。 C009&#xff1a;是通用版 B0014&#xff1a;是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存&#xff1a;1GB 以上 硬盘&#xf…...

c# 局部函数 定义、功能与示例

C# 局部函数&#xff1a;定义、功能与示例 1. 定义与功能 局部函数&#xff08;Local Function&#xff09;是嵌套在另一个方法内部的私有方法&#xff0c;仅在包含它的方法内可见。 • 作用&#xff1a;封装仅用于当前方法的逻辑&#xff0c;避免污染类作用域&#xff0c;提升…...

Python 高效图像帧提取与视频编码:实战指南

Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...

LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)

在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...

PH热榜 | 2025-06-08

1. Thiings 标语&#xff1a;一套超过1900个免费AI生成的3D图标集合 介绍&#xff1a;Thiings是一个不断扩展的免费AI生成3D图标库&#xff0c;目前已有超过1900个图标。你可以按照主题浏览&#xff0c;生成自己的图标&#xff0c;或者下载整个图标集。所有图标都可以在个人或…...

leetcode73-矩阵置零

leetcode 73 思路 记录 0 元素的位置&#xff1a;遍历整个矩阵&#xff0c;找出所有值为 0 的元素&#xff0c;并将它们的坐标记录在数组zeroPosition中置零操作&#xff1a;遍历记录的所有 0 元素位置&#xff0c;将每个位置对应的行和列的所有元素置为 0 具体步骤 初始化…...

boost::filesystem::path文件路径使用详解和示例

boost::filesystem::path 是 Boost 库中用于跨平台操作文件路径的类&#xff0c;封装了路径的拼接、分割、提取、判断等常用功能。下面是对它的使用详解&#xff0c;包括常用接口与完整示例。 1. 引入头文件与命名空间 #include <boost/filesystem.hpp> namespace fs b…...

高端性能封装正在突破性能壁垒,其芯片集成技术助力人工智能革命。

2024 年&#xff0c;高端封装市场规模为 80 亿美元&#xff0c;预计到 2030 年将超过 280 亿美元&#xff0c;2024-2030 年复合年增长率为 23%。 细分到各个终端市场&#xff0c;最大的高端性能封装市场是“电信和基础设施”&#xff0c;2024 年该市场创造了超过 67% 的收入。…...