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

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)是一个非常重要的概念,它提供了一种松耦合的方式,让对象间能够通知彼此,而无需直接联系。事件的使用可以让我们的代码更加灵活、可扩展且易于维护。 事件可以视作委托的实例&…...

《第三代大语言模型Grok 3:闪亮登场》

《第三代大语言模型Grok 3:闪亮登场》 在科技飞速发展的今天,人工智能领域的每一次重大突破都如同巨石投入平静湖面,激起千层浪。当地时间 2 月 15 日,马斯克在社交平台 X 上投下了这样一颗 “巨石”,他宣布旗下人工智能公司 xAI 开发的第三代大语言模型 Grok 3,将于北京…...

rem、em、vw区别

在前端开发里&#xff0c;rem、em、vw都是用来设置元素大小的单位&#xff0c;下面就用大白话讲讲它们的区别。 参考标准不一样 rem&#xff1a;就像大家都用同一把“大尺子”来量东西&#xff0c;这把“大尺子”就是网页里根元素&#xff08;也就是 <html> 标签&#…...

最新Apache Hudi 1.0.1源码编译详细教程以及常见问题处理

1.最新Apache Hudi 1.0.1源码编译 2.Flink、Spark、Hive集成Hudi 1.0.1 3.flink streaming写入hudi 目录 1. 版本介绍 2. 安装maven 2.1. 下载maven 2.2. 设置环境变量 2.3. 添加Maven镜像 3. 编译hudi 3.1. 下载hudi源码 3.2. 修改hudi源码 3.3. 修改hudi-1.0.1/po…...

C语言简单练习题

文章目录 练习题一、计算n的阶乘bool类型 二、计算1!2!3!...10!三、计算数组arr中的元素个数二分法查找 四、动态打印字符Sleep()ms延时函数system("cls")清屏函数 五、模拟用户登录strcmp()函数 六、猜数字小游戏产生一个随机数randsrandRAND_MAX时间戳time() 示例 …...

C++ ——static关键字

1、static修饰局部变量---->静态局部变量 特点&#xff1a; &#xff08;1&#xff09;第一次调用时创建&#xff0c;然后第一次调用结束时没有被回收&#xff0c;以后每次调用&#xff0c;都延用上一次调用结束时的值&#xff0c;直到程序结束时&#xff0c;才被回收 &a…...

Jasper AI技术浅析(二):语言模型

Jasper AI 的核心语言模型是基于 OpenAI 的 GPT-4(Generative Pre-trained Transformer 4)构建的。GPT-4 是一种生成式预训练语言模型,利用深度学习技术,特别是 Transformer 架构,来理解和生成自然语言。 1. GPT-4 的基本原理 1.1 Transformer 架构 GPT-4 基于 Transfo…...

QML 部件获得焦点触发的全局槽函数 onActiveFocusItemChanged

在qml的window窗口中&#xff0c;假如添加里许多其他部件&#xff0c;当这些部件改变时&#xff0c;会有一个全局部件焦点改变槽函数触发&#xff0c;就是 onActiveFocusItemChanged 可以通过此槽函数就可以知道当前焦点在哪一个部件上&#xff0c;也可以做一些自动化测试等&…...

【git】工作场景下的 工作区 <-> 暂存区<-> 本地仓库 命令实战 具体案例

&#x1f680; Git 工作区 → 暂存区 → 本地仓库 → 回退实战 Git 的核心流程是&#xff1a; &#x1f449; 工作区&#xff08;Working Directory&#xff09; → git add → 暂存区&#xff08;Staging Area&#xff09; → git commit → 本地仓库&#xff08;Local Repos…...

Python 中从零开始的随机梯度下降

文章目录 一、说明二、了解基础知识的重要性&#xff1a;2.1 什么是梯度下降&#xff1f;2.2 梯度下降的类型&#xff1a; 三、随机梯度下降 &#xff08;SGD&#xff09; 有何不同3.1 随机性的概念&#xff1a;3.2 SGD的优点和缺点&#xff1a; 四、随机梯度下降的分步说明五、…...

期权隐含波动率是什么意思?

财顺小编本文主要介绍期权隐含波动率是什么意思&#xff1f;期权隐含波动率&#xff08;Implied Volatility&#xff09;是根据当前期权市场价格&#xff0c;利用期权定价模型&#xff08;如Black-Scholes模型&#xff09;推导出的关于合约标的理论上的价格波动率。它反映了市场…...

python中使用数据库sqlite3

Python使用sqlite3数据库 python3.x标准库内置了SQLite3 查看sqlite的版本 import sqlite3 sqlite_version sqlite3.sqlite_version print(f"SQLite version: {sqlite_version}") 显示 导入模块连接sqlitte3 import sqlite3 consqlite3.connect("d:/fi…...

JavaScript数组-数组的概念

在JavaScript编程中&#xff0c;数组&#xff08;Array&#xff09;是一种非常重要的数据结构&#xff0c;它允许我们将多个值存储在一个单独的变量中。数组可以包含任意类型的元素&#xff0c;如数字、字符串、对象甚至是其他数组&#xff0c;并提供了丰富的内置方法来操作这些…...

英语---基础词汇库

〇、动词类&#xff08;常见谓语表述&#xff09; 1.show&#xff0c;indicate&#xff0c;find 认为&#xff0c;表明 2.improve,promote,boost,enhance,increase&#xff0c;advocate&#xff0c;strength 改善&#xff0c;提升&#xff0c;促进&#xff0c;增强&#xff0…...

ASCII 与 Unicode:两种字符编码的定义和不同

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: Java 文章目录 &#x1f4af;前言&#x1f4af;一、ASCII编码详解1.1 ASCII的组成1.2 ASCII的局限性 &#x1f4af;二、Unicode编码详解2.1 Unicode编码方式2.2 Unicode的字符范围 &#x1f4af;三、ASCII与Unicode的…...

Linux相关概念和易错知识点(28)(线程控制、Linux下线程的底层)

目录 1.线程控制 &#xff08;1&#xff09;pthread和thread库 &#xff08;2&#xff09;线程的创建、等待和分离 ①线程创建 ②线程等待 ③线程分离 ④线程替换&#xff08;不可行&#xff09; &#xff08;3&#xff09;线程的终止和取消 ①线程终止 ②线程取消 2…...

lighten() 函数被弃用:替代方案color.scale()或者color.adjust()

在 SCSS (Sass 的一个语法) 中&#xff0c;lighten() 函数用于调整颜色的亮度。然而&#xff0c;随着 Sass 语言的不断发展&#xff0c;一些旧函数被标记为弃用&#xff0c;以鼓励使用更现代、更灵活的 API。lighten() 函数就是其中之一。 1. 弃用通知 当您看到 lighten() is…...

【leetcode】双指针:有效三角形的个数 and 和为s的两个数

文章目录 1. 有效三角形的个数1.题目2.讲解算法原理3.代码 2.和为s的两个数1.题目2.思路3.代码 1. 有效三角形的个数 1.题目 示例1解析&#xff1a; 2.讲解算法原理 3.代码 class Solution { public:int triangleNumber(vector<int>& nums) {sort(nums.begin(), …...

IDEA通过Contince接入Deepseek

Deepseek 的出色表现&#xff0c;上期【Deepseek得两种访问方式与本地部署】 安装Continue插件 第一步、下载插件 在编辑栏【File】->设置【Settiings】或快捷键【CtrlAltS】,弹窗的左侧导航树&#xff0c;选择【plugins】,在marketplace 搜索【Continue】&#xff0c;点…...

grep如何排除多个目录?

在使用 grep 进行文本搜索时&#xff0c;有时候需要排除多个目录&#xff0c;避免在这些目录下进行搜索。下面介绍几种不同的实现方式。 目录 1.使用 -r 和 --exclude-dir 选项&#xff08;GNU grep&#xff09; 2.使用扩展正则表达式和 -P 选项&#xff08;GNU grep&#x…...

GB28181录像回放实战:从SIP信令到PS流解析,一个完整抓包案例的拆解

GB28181录像回放全链路解析&#xff1a;从SIP信令到PS流解包实战手册 在视频监控系统集成领域&#xff0c;GB28181协议堪称"普通话"级别的存在。但当我们真正动手实现录像回放功能时&#xff0c;往往会陷入协议细节的迷宫——为什么设备不响应我的时间范围请求&…...

第一章-01-初识对象

1.生活中或是程序中&#xff0c;我们都可以使用设计表格、生产表格、填写表格的形式组织数据2.在程序中设计表格&#xff0c;称之为&#xff1a;设计类&#xff08;class&#xff09;打印表格&#xff0c;称之为&#xff1a;创建对象填写表格&#xff0c;称之为&#xff1a;对象…...

使用Hermes Agent时如何将其后端切换至Taotoken平台

使用 Hermes Agent 时如何将其后端切换至 Taotoken 平台 1. 准备工作 在开始配置之前&#xff0c;请确保您已经拥有 Taotoken 平台的 API Key 和想要使用的模型 ID。您可以在 Taotoken 控制台的「API 密钥」页面创建新的密钥&#xff0c;并在「模型广场」查看可用的模型列表及…...

MySQL 8.0.12安装后必做的5件事:安全加固、性能调优与可视化工具推荐

MySQL 8.0.12安装后必做的5件事&#xff1a;安全加固、性能调优与可视化工具推荐 刚完成MySQL 8.0.12安装的开发者常会遇到这样的困惑&#xff1a;明明按照教程一步步操作&#xff0c;为什么数据库用起来总觉得不够顺手&#xff1f;命令行操作繁琐、默认配置性能平平、安全隐患…...

【国家级医疗平台配置白皮书】:基于37家三甲医院实测数据的Python环境隔离与版本锁策略

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;国家级医疗平台Python环境配置的背景与挑战 国家级医疗平台承载着全民健康档案管理、跨区域诊疗协同、AI辅助诊断模型部署等关键任务&#xff0c;其后端服务高度依赖稳定、可审计、合规的Python运行时…...

2026年如何集成Hermes Agent/OpenClaw?京东云萌新速成4分钟部署及接入百炼APIKey教程

2026年如何集成Hermes Agent/OpenClaw&#xff1f;京东云萌新速成4分钟部署及接入百炼APIKey教程。本文面向零基础用户&#xff0c;完整说明在轻量服务器与本地Windows11、macOS、Linux系统中部署OpenClaw&#xff08;Clawdbot&#xff09;的流程&#xff0c;包含环境配置、服务…...

终极RPG Maker MV/MZ插件集合:500+免费插件打造专业级游戏开发体验

终极RPG Maker MV/MZ插件集合&#xff1a;500免费插件打造专业级游戏开发体验 【免费下载链接】RPGMakerMV RPGツクールMV、MZで動作するプラグインです。 项目地址: https://gitcode.com/gh_mirrors/rp/RPGMakerMV 你是否曾为RPG Maker有限的游戏机制而苦恼&#xff1f…...

【独家首发】Python风控配置性能压测报告:YAML vs TOML vs JSONSchema,吞吐量差异达4.8倍!

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;Python风控配置的演进与压测背景 早期 Python 风控系统多依赖硬编码规则与静态 YAML 配置&#xff0c;如 risk_rules.yaml 中直接定义阈值和动作&#xff0c;缺乏运行时动态加载与热更新能力。随着微服…...

Mac M1芯片上VSCode编译C++报错?手把手教你搞定‘_main‘未定义符号(arm64架构)

Mac M1芯片VSCode编译C报错全攻略&#xff1a;从_main未定义到arm64架构深度解析 第一次在M1芯片的Mac上打开VSCode写C代码时&#xff0c;那种兴奋感很快被满屏红色错误信息浇灭。"Undefined symbols for architecture arm64: _main"——这个看似简单的报错背后&…...

从FPGA到ASIC:偶数分频器的那些‘坑’与实战调试技巧(附Modelsim仿真波形分析)

从FPGA到ASIC&#xff1a;偶数分频器的那些‘坑’与实战调试技巧&#xff08;附Modelsim仿真波形分析&#xff09; 时钟分频电路是数字IC设计中最基础却最容易出问题的模块之一。记得我第一次独立负责一个FPGA项目时&#xff0c;就因为二分频电路的异步复位问题导致整个系统时钟…...