c#中“事件-event”的经典示例与理解
在C#编程语言中,事件(Event)是一个非常重要的概念,它提供了一种松耦合的方式,让对象间能够通知彼此,而无需直接联系。事件的使用可以让我们的代码更加灵活、可扩展且易于维护。
事件可以视作委托的实例,因此熟悉委托的使用对理解事件是有帮助的(如果学过C语言,委托就相当于定义一个指向函数的指针类型,事件就相当于指向函数的指针变量);不熟悉委托也不影响本文的阅读。
本文将通过一个经典的生活案例“闹钟一响,打工人就要起床了”来讲解C#中事件(Event)的基本概念和使用方法。
一、事件的基本概念
通过事件,可以让一个类(发布者c1)向另一个类(订阅者c2)发出通知,告诉它某个特定操作已经发生。
事件的核心问题是:如何用发布者c1中的方法f1去调用订阅者c2中的方法f2。
基本的思路是:在c1中定义一个指向f2方法的变量v,然后在f1中调用v。这样就实现了通过c1中的方法f1来调用c2中的方法f2。
我给概括为以下几个要素:
2个类:发布者、发送者 → c1订阅者、接收者 → c2
2个方法:触发事件的方法 → f1(发布者)事件的处理方法 → f2(订阅者)
1个变量:事件 → v(在c1中定义,但不在c1中赋值)
4个关键:事件的类型 → q1事件的赋值 → q2事件的触发 → f2的调用 → q3触发的调用 → f1的调用 → q4
二、经典案例:“闹钟响了,打工人就要起床了”
用“闹钟响了,打工人就要起床了”作为一个现实中的例子,来模拟C#中的事件。假设我们有一个闹钟(发布者)和一个打工人(订阅者)。每当闹钟响起时,打工人就要起床上班了。这个过程中的关键是,打工人并不直接控制闹钟,而是通过事件来接收到闹钟响起的通知,从而作出反应。
三、用C#模拟“闹钟响了,打工人就要起床了”
下面是实现这个经典案例C#代码:
using System;namespace Demo
{class Program{static void Main(string[] args){Alarms alarm = new Alarms();Workers worker = new Workers();alarm.OnRingSound = worker.getupWorker; //4个问题之q2→事件的赋值Console.Write("请输入闹钟参数(表示闹钟响的次数):");uint nClock = Convert.ToUInt16(Console.ReadLine());alarm.ringAlarm(nClock); //4个问题之q4→触发的调用→f1的调用Console.ReadLine();}}public class Alarms //2个类之c1:发布者{public delegate void RingEvent(); //4个问题之q1→事件的类型public RingEvent OnRingSound; //1个变量之事件vpublic void ringAlarm(uint ringKind) //2个方法之f1:触发事件的方法{Console.Write($"闹钟响{ringKind}次了。");OnRingSound(); //4个问题之q3→事件的触发→f2的调用}}public class Workers //2个类之c2:订阅者{public void getupWorker() //2个方法之f2:事件的处理方法{Console.WriteLine($"打工人,起床!");}}
}
四、为什么使用事件?
在进行事件的赋值时,使用+=符号,而不是直接使用=符号,或者赋值一个空引用,这些操作会对事件的安全性造成威胁。
为了解决这个问题,C#提供了专门的事件处理机制,以保证事件订阅的可靠性。事件通过在委托声明中添加event关键字来实现,如下所示:
public event RingEvent OnRingSound;
此时,以下代码会出现编译错误:
alarm.OnRingSound = worker.getupWorker; // 编译错误:不能直接赋值alarm.OnRingSound = null; // 编译错误:不能直接赋值为null
而通过订阅事件时,可以使用+=来注册事件处理方法:
alarm.OnRingSound += worker.getupWorker; // 正确:订阅事件alarm.OnRingSound -= worker.getupWorker; // 正确:取消订阅事件
下面是“event”正统代码
using System;namespace Demo
{class Program{static void Main(string[] args){Alarms alarm = new Alarms();Workers worker = new Workers();worker.subscribeToRing(alarm); //类作为实参进行传递时是地址传递Console.Write("请输入闹钟参数(表示闹钟响的次数):");uint nClock = Convert.ToUInt16(Console.ReadLine());alarm.ringAlarm(nClock); //4个问题之q4→触发的调用→f1的调用Console.ReadLine();}}public class Alarms //2个类之c1:发布者{public delegate void RingEvent(); //4个问题之q1→事件的类型public event RingEvent OnRingSound; //1个变量之事件vpublic void ringAlarm(uint ringKind) //2个方法之f1:触发事件的方法{Console.Write($"闹钟响{ringKind}次了。");OnRingSound(); //4个问题之q3→事件的触发→f2的调用}}public class Workers //2个类之c2:订阅者{public void subscribeToRing(Alarms a) {a.OnRingSound += getupWorker; //4个问题之q2→事件的赋值}public void getupWorker() //2个方法之f2:事件的处理方法{Console.WriteLine($"打工人,起床!");}}
}
进一步概括
2个类:发布者、发送者 → c1订阅者、接收者 → c2
2个方法:触发事件的方法 → f1(发布者,c1中)事件的处理方法 → f2(订阅者,c2中)
1个变量:事件 → v(在c1中定义,在c2中赋值)
4个关键:事件的类型 → q1 → c1中事件的赋值 → q2 → c2中事件的触发 → f2的调用 → q3 → c1中触发的调用 → f1的调用 → q4 → c1、c2外
发布者的实例化
订阅者的实例化订阅者订阅
4个问题之q4→触发的调用→f1的调用2个类之c1:发布者
{4个问题之q1→事件的类型1个变量之事件v2个方法之f1:触发事件的方法{4个问题之q3→事件的触发→f2的调用}
}2个类之c2:订阅者
{订阅的方法{4个问题之q2→事件的赋值}2个方法之f2:事件的处理方法{}
}
五、总结
通过这个经典案例“闹钟响了,打工人就要起床了”,我们可以看到事件如何使得C#程序中的各个部分保持松耦合,发布者与订阅者之间并不直接依赖,而是通过事件机制进行通信。这种设计使得我们的代码更加灵活和可扩展。
后续的系统性学习可以看【C#从入门到精通(第6版)】的《17.5 事件》或【叩响C#之门】的《16.4 事件处理机制》。
参考
清华大学出版社-图书详情-《C#从入门到精通(第6版)》
叩响C#之门 (豆瓣)
C# 事件(Event) | 菜鸟教程
C#事件--全网最全+全网最易理解-CSDN博客
ChatGPT | OpenAI
相关文章:
c#中“事件-event”的经典示例与理解
在C#编程语言中,事件(Event)是一个非常重要的概念,它提供了一种松耦合的方式,让对象间能够通知彼此,而无需直接联系。事件的使用可以让我们的代码更加灵活、可扩展且易于维护。 事件可以视作委托的实例&…...
如何画产品功能图、结构图
功能图的类型 常见的功能图包括数据流图、用例图、活动图、状态图、类图、组件图、部署图等等,不同的应用场景和目标下,需要确定不同的功能图类型。 数据流图 用例图 状态图 类图 组件图 组件图是由软件系统、组件和组件之间的关系组成的图形…...
标准输入输出流,面向对象,构造函数
标准输入输出流 为什么不直接用printf和scanf? 不能输入/输出C新增的内容 std C的一些标识符,都是定义在std这个名字空间下面cout 是什么? 1.是一个ostream对象 output stream:输出流使用 <<:输出流运算符 作用:将右边…...
Vue2 中使用 UniApp 时,生命周期钩子函数总结
在 Vue2 中使用 UniApp 时,生命周期钩子函数是一个重要的概念。它允许开发者在特定的时间点运行代码,管理组件的生命周期。以下是 Vue2 中 UniApp 常用的生命周期钩子函数总结: 1. beforeCreate 说明: 组件实例刚被创建,此时数据…...
QEMU源码全解析 —— 内存虚拟化(12)
接前一篇文章:QEMU源码全解析 —— 内存虚拟化(11) 本文内容参考: 《趣谈Linux操作系统》 —— 刘超,极客时间 《QEMU/KVM》源码解析与应用 —— 李强,机械工业出版社 QEMU内存管理模型...
如何将ubuntu下的一个目录,保存目录结构为一个git仓库并上传
目录 1. 初始化本地Git仓库 2. 添加文件到仓库 3. 提交更改 4. 创建并关联远程仓库 5. 推送代码到远程仓库 完整流程总结 要将Ubuntu下的一个目录(例如rpc)保存为一个Git仓库并上传到远程仓库,您可以遵循以下步骤: 1. 初始…...
深度学习中通道数的理解
目录 一、通道(Channels)的作用 1. 表示输入数据的多样性 2. 提取多层次特征 3. 信息融合与交互 4. 控制模型的复杂度 5. 支持多任务学习 6. 实际应用中的通道设计 7. 总结 二、案例一 1. 输入图像的通道(RGB) 2. 输出特…...
2025寒假天梯赛训练5
L1-3 敲笨钟 - 2025寒假天梯赛训练5 思路:一般ex的模拟题,主要是找好空格的位置进行修改替换。 #include <bits/stdc.h> using namespace std; #define int long long #define endl "\n" #define sz(x) (int)x.size() #define e empla…...
PowerBI 矩阵 列标题分组显示(两行列标题)
先看效果 数据表如下: 我们在powerbi里新建一个矩阵,然后如图加入字段: 我们就会得到这样的矩阵: 我们在“可视化”->“列”,上双击,输入空格,就能消除左上角的"类别"两字 同理修…...
服务器部署DeepSeek,通过Ollama+open-webui部署
1. 安装ollama 1.1. linux 安装 Ollama是目前常用的AI模式部署的第三方工具,能一键部署deepSeek Ollama官方网址https://ollama.com/ 选择Download下载对应的服务版本 服务器选择Linux,下面是下载代码 curl -fsSL https://ollama.com/install.…...
PVE 磁盘管理详解:从 Windows 到 Linux 的思维转换(文末附资源)
Proxmox VE(PVE)是一款基于 Debian Linux 的虚拟化平台,其文件系统管理与 Windows 差异较大,尤其是磁盘和文件夹的设计逻辑。本文将以通俗易懂的方式,详解 PVE 中磁盘管理的核心操作,并对比 Windows 帮助大…...
Ubuntu 连接 air pods
1. sudo vim /etc/bluetooth/main.conf , 修改蓝牙模式为blder 2.sudo /etc/init.d/bluetooth restart, 重启蓝牙,即可连接成功...
【LeetCode Hot100 矩阵】矩阵置零、螺旋矩阵、旋转图像、搜索二维矩阵II
矩阵 1. 矩阵置零(Set Matrix Zeroes)解题思路步骤: 代码实现 2. 螺旋矩阵(Spiral Matrix)解题思路具体步骤: 代码实现 3. 旋转矩阵 90 度解决思路代码实现 5. 搜索二维矩阵中的目标值解决思路代码实现 1. …...
民用无人驾驶航空器操控员考试
1. 注册 民用无人驾驶航空器综合管理平台 (caac.gov.cn) 2. 选择 操控员资质 3. 安全操控理论培训 -> 在线视频培训 学习完后选择 【在线考试】 共 50道 单项 选择题,每选项3个,80分及格。 4. 查看 我的合格证 证书有效期2年...
TCP可靠传输的ARQ协议
基本知识 ARQ(Automatic Repeat-reQuest)协议主要包含:停等ARQ协议、连续ARQ协议,其中连续ARQ协议是为了解决停等ARQ协议信道利用率低的问题,目前传统的连续ARQ协议有回退N帧ARQ协议、选择性重传ARQ协议。 注意&#…...
秋招春招投递记录——2024
公司链接待办截止日期已完成状态携程集团(上海)https://campus.ctrip.com/campus-recruitment/简历挂微众银行(武汉)https://campus.webank.com/campus-recruitment/webankhr/测评腾讯云智(重庆)https://jo…...
前端与后端的对接事宜、注意事项
前端与后端的对接事宜、注意事项 一、对接核心流程(完整生命周期) #mermaid-svg-6yzij6OD8DKqiMLD {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-6yzij6OD8DKqiMLD .error-icon{fill:#552222;}#mermaid-svg-6yzi…...
002 第一个python程序
编程语言 编程语言可以做的事情: 网站开发、软件 、游戏、APP、 小程序、 爬虫、 数据分析、脚本 第一个python程序 找到IDE图标pycharm 新建项目 选择项目路径 创建目录 新建python文件 输入代码 运行程序查看结果 print 介绍 print : 输出内容…...
频率自适应扩张卷积(FADC)详解及代码复现
背景介绍 在介绍频率自适应扩张卷积(FADC)之前,我们需要了解卷积神经网络(CNN)在处理复杂图像任务时面临的挑战。CNN的成功主要依赖于其多层结构和卷积层的设计,这些设计可以有效地捕捉图像的局部特征。然而,随着网络层数的增加,感受野的大小也随之增加,这可能导致一…...
解锁机器学习核心算法 | 决策树:机器学习中高效分类的利器
引言 前面几篇文章我们学习了机器学习的核心算法线性回归和逻辑回归。这篇文章我们继续学习机器学习的经典算法——决策树(Decision Tree) 一、决策树算法简介 决策树算法是一种典型的分类方法,也是一种逼近离散函数值的方法。它的核心思想…...
数据结构——顺序表与链表
目录 前言 一线性表 二顺序表 1实现 2相关面试题 2.1移除元素 2.2删除有序数组中的重复项 3.3合并两个有序数组 3问题 三链表 1链表的分类 1.1单向或者双向 1.2带头或者不带头 1.3循环或者非循环 2实现 2.1尾插与头插 2.2尾删与头删 2.3pos前插入节点与删除…...
在 Python 中使用 Ollama API
文章目录 一、环境准备二、使用方法1.简单对话2.流式响应3.结构化输出4.自定义客户端4.1 同步客户端4.2 异步客户端4.3 同步 & 异步客户端不同调用次数耗时对比测试 三、常用的ollama API 接口聊天生成本地模型列表显示模型信息创建模型复制模型删除模型拉取模型推送模型生…...
BGP配置华为——RR反射器配置
实验拓扑 与之前实验同理将loop0作为routerID使用,且R1和R2上用loop1接口用于模拟用户其他网段 实验要求 1,在AS100内运行OSPF协议 2.配置路由反射器,使得从R1进入的数据能够反射到全局网络 3.在R1和R2上分别宣告自己的loop1口网段用于观…...
一.AI大模型开发-初识机器学习
机器学习基本概念 前言 本文主要介绍了深度学习基础,包括机器学习、深度学习的概念,机器学习的两种典型任务分类任务和回归任务,机器学习中的基础名词解释以及模型训练的基本流程等。 一.认识机器学习 1.人工智能和机器学习 人工智能&am…...
力扣做题记录 (二叉树)
二叉树 打算先来了解二叉树基础,都是简单题,目的是熟悉代码格式和解题基础思路。 1、二叉树最大深度 二叉树最大深度 方法一、深度搜索 直接用原函数做递归,比较简单 /*** Definition for a binary tree node.* struct TreeNode {* …...
国内情智机器人:从“通情达理”到温暖陪伴的跨越
近年来,随着人工智能技术的飞速发展,情智机器人(具备情感智能的机器人)逐渐成为国内研究和应用的热点领域。从情感计算的基础研究到人形机器人的实际应用,国内在这一领域取得了显著进展,展现出巨大的发展潜力。情智机器人不仅能够理解人类的情感,还能以温暖的方式提供陪…...
Unity中如何判断URL是否为RTSP或RTMP流
技术背景 如何在Unity中判断一个字符串URL是否是RTSP或RTMP流。首先,RTSP通常以“rtsp://”开头,而RTMP则是“rtmp://”或者有时是“rtmps://”用于安全连接。 接下来,如何在C#中进行字符串的检查。最简单的方法应该是检查URL是否以这些协议…...
前端里的this指向问题
目录 1.代码输出结果 2.代码输出结果 3.代码输出结果 4.代码输出结果 5.代码输出结果 6.代码输出结果 7.代码输出结果 8.代码输出结果 9.代码输出结果 10.代码输出结果 11.代码输出结果 12.代码输出结果 13.代码输出结果 14.代码输出结果 总结 1.代码输出结果 f…...
deepseek与gpt,核心原理对比
DeepSeek与GPT作为AI大模型,在自然语言处理等领域展现出强大的能力,它们的核心原理对比主要体现在模型架构、训练策略、资源效率以及应用场景优化等方面。 一、模型架构 DeepSeek 混合专家(MoE)框架:DeepSeek采用了混合专家框架,其内部包含多个“专家”子模块,每个子模…...
提示工程实现数据质量评估
提示工程实现数据质量评估 我准备先查看数据集的基本信息和内容,从完整性、准确性、一致性等方面评价数据质量,再依据数据规模、质量和潜在价值等因素进行定价。 import pandas as pd# 读取文件 excel_file = pd.ExcelFile(/mnt/a-极速-脑筋-98.xls)# 获取所有表名 sheet_n…...
