图解C#高级教程(二):事件

在现实生活当中,有一些事情发生时,会连带另一些事情的发生。例如,当某国的总统发生换届时,不同党派会表现出不同的行为。两者构成了“因果”关系,因为发生了A,所以发生了B。在编程语言当中,具有类似的概念。在编程语言中,发生的事情 A 称为事件,因 A 发生的事情 B 称为对事件的处理或者响应(事件处理程序)。

本章主要讲解 C# 语言当中的发布者-订阅者模式的概念、代码实现该模式时的组成部分以及标准事件的用法。

1. 发布者和订阅者的概念

在程序中,我们很多时候会面临这样一个需求:当一个特定的程序事件发生时,程序的其它部分(类、函数或者其它)可以得到该事件已经发生的通知并对此做出相应的处理。

发布者-订阅者模式(publisher/subscriber pattern)可以满足这种需求。在这种模式中,发布者类定义了订阅者类感兴趣的事件。当事件发生时,发布者通知到这些订阅者,然后订阅者执行相应的事件处理函数。

但是,发布者是如何通知到这些订阅者呢?订阅者通过注册函数。其实就是发布者维护了一个关注某个事件的订阅者集合。例如,我在 csdn 博客上开了某个领域的专栏,然后你订阅了这个专栏,在后台会维护关注这个专栏的用户列表。当我更新了这个专栏的文章时,会通知到每一个订阅的用户。

那么,订阅者又是如何在事件发生时执行相应的事件处理函数呢?订阅者向发布者注册事件发生时的事件处理函数(回调函数,我们称函数的参数类型是函数的函数)。

下图说明了发布者-订阅者模式的工作流程:
在这里插入图片描述
下面是发布者-订阅者模式的组件:

  1. 发布者:发布某个事件的类或结构。维护一个订阅者集合(可选)、一个事件处理程序的集合、提供给订阅者注册订阅和回调函数的接口;
  2. 订阅者:关注事件的类或者结构。需要向订阅者提供回调函数名;
  3. 触发事件。本质上是触发事件的代码。

在 C#高级教程(一):委托当中介绍了委托。实际上,事件就像是专门用于某种特殊用途的委托。

2. 源代码组件

为了实现发布者-订阅者模式,在代码中需要完成以下 5 部分:

  • 委托类型声明:事件和事件处理程序必须具有相同的签名和返回类型,它们通过委托类型进行描述;
  • 事件处理程序声明:订阅者类中会在事件触发时执行的方法调用;
  • 事件声明:发布者类必须声明一个订阅者类可以注册的事件成员;
  • 事件注册:订阅者必须订阅事件才能在它被触发时得到通知;
  • 触发事件的代码:发布者类中“触发”事件并执行所有事件处理程序的代码。
    在这里插入图片描述
    下面是一个简单的代码实现:
delegate void Handler();class Incrementer
{public event Handler CountedADozen;     // 事件名public void DoCount(){for (int i = 1; i < 100; i++){if (i % 12 == 0 && CountedADozen!= null) // 触发事件的代码{CountedADozen();        // 调用事件}}}
}// 订阅者
class Dozens
{public int DozensCount { get; set; }public Dozens(Incrementer incrementer){DozensCount = 0;incrementer.CountedADozen += IncrementDozenCount;   // 注册回调函数}// 回调函数void IncrementDozenCount(){DozensCount++;}
}class Programer
{static void Main(){Incrementer incrementer = new Incrementer();Dozens dozens = new Dozens(incrementer);incrementer.DoCount();Console.WriteLine("Dozens count: {0}", dozens.DozensCount);}
}

输出:
在这里插入图片描述

3. 标准事件的用法

由于 GUI 编程是事件驱动的,而 Windows GUI 编程广泛地使用了事件,因此 .NET 框架提供了一个标准模式。具体就是在 System 命名空间提供了 EventHandler 委托类型。
在这里插入图片描述
第二个参数 EventArgs 设计为不能用来传递任何数据,它用于不需要传递数据的事件处理程序。如果你希望传递数据,必须声明一个派生自 EventArgs 的类,使用合适的字段来保存需要传递的数据。

接下来,我们就使用标准事件改写第二节的程序:

class Incrementer
{public event EventHandler CountedADozen;     // 事件名public void DoCount(){for (int i = 1; i < 100; i++){if (i % 12 == 0 && CountedADozen!= null) // 触发事件的代码{CountedADozen(this, null);        // 调用事件}}}
}
// 订阅者
class Dozens
{public int DozensCount { get; set; }public Dozens(Incrementer incrementer){DozensCount = 0;incrementer.CountedADozen += IncrementDozenCount;   // 注册回调函数}// 回调函数void IncrementDozenCount(object sender, EventArgs e){DozensCount++;}
}class Programer
{static void Main(){Incrementer incrementer = new Incrementer();Dozens dozens = new Dozens(incrementer);incrementer.DoCount();Console.WriteLine("Dozens count: {0}", dozens.DozensCount);}
}

通过扩展 EventArgs 来传递数据

为了向自己的事件处理程序的第二个参数传入数据,并且又符合标准惯例,我们需要声明一个派生自 EventArgs 的自定义类,用来保存我们需要传入的数据。

下面是改写后的代码:

// 派生自EventArgs的自定义类
public class IncrementerArgs: EventArgs
{public int IterationCount { get; set; }
}class Incrementer
{// 使用自定义类的泛型委托public event EventHandler<IncrementerArgs> CountedADozen;     // 事件名public void DoCount(){IncrementerArgs args = new IncrementerArgs();for (int i = 1; i < 100; i++){if (i % 12 == 0 && CountedADozen!= null) // 触发事件的代码{args.IterationCount = i;CountedADozen(this, args);        // 调用事件}}}
}
// 订阅者
class Dozens
{public int DozensCount { get; set; }public Dozens(Incrementer incrementer){DozensCount = 0;incrementer.CountedADozen += IncrementDozenCount;   // 注册回调函数}// 回调函数void IncrementDozenCount(object sender, IncrementerArgs e){Console.WriteLine("Incremented at iteration: {0} and {1}",e.IterationCount, sender.ToString());DozensCount++;}
}class Programer
{static void Main(){Incrementer incrementer = new Incrementer();Dozens dozens = new Dozens(incrementer);incrementer.DoCount();Console.WriteLine("Dozens count: {0}", dozens.DozensCount);}
}

输出:
在这里插入图片描述

移除事件处理程序

在用完了事件处理程序之后,可以从事件中把它移除。下面是一个例子:

class Publisher
{public event EventHandler SimpleEvent;public void RaiseTheEvent() { SimpleEvent(this, null); }
}class Subsriber
{public void MethodA(object o, EventArgs e) { Console.WriteLine("MethodA called"); }public void MethodB(object o, EventArgs e) { Console.WriteLine("MethodB called"); }}class Program
{static void Main(){Publisher p = new Publisher();Subsriber s = new Subsriber();p.SimpleEvent += s.MethodA;p.SimpleEvent += s.MethodB;p.RaiseTheEvent();Console.WriteLine("\r\nRemove MethodB");p.SimpleEvent -= s.MethodB;p.RaiseTheEvent();}
}

程序的输出:
在这里插入图片描述

小结:本章介绍了 C# 语言当中的发布者-订阅者模式的概念,源代码组件的五个部分,以及标准事件的用法。

各位道友,码字不易。如有收获,记得一键三连。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/438416.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android问题笔记五十:构建错误-AAPT2 aapt2-7.0.2-7396180-windows Daemon

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分…

Visual Studio 字体与主题推荐

个人推荐&#xff0c;仅供参考&#xff1a; 主题&#xff1a;One Monokai VS Theme 链接&#xff1a;One Monokai VS Theme - Visual Studio Marketplacehttps://marketplace.visualstudio.com/items?itemNameazemoh.onemonokai 效果&#xff1a; 字体&#xff1a;JetBrain…

SpringBoot项目请求不中断动态更新代码

在开发中&#xff0c;有时候不停机动态更新代码热部署是一项至关重要的功能&#xff0c;它可以在请求不中断的情况下下更新代码。这种方式不仅提高了开发效率&#xff0c;还能加速测试和调试过程。本文将详细介绍如何在 Spring Boot 项目在Linux系统中实现热部署&#xff0c;特…

《业务三板斧:定目标、抓过程、拿结果》读书笔记1

这个书是24年新书&#xff0c;来自阿里系的人的作品&#xff0c;还可以。今天先看前沿部分的精彩部分&#xff1a; 我们在服务企业的过程中&#xff0c;发现了一个常见的管理现象&#xff1a;管理者自 己承担了团队里重要的项目&#xff0c;把风险和压力都集中在自己身上。因 此…

报刊订阅系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;报刊类型管理&#xff0c;报刊信息管理&#xff0c;报刊订阅管理&#xff0c;订阅发送管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;报刊信息&a…

<<迷雾>> 第7章 会变魔术的触发器(1)--连着两个按键开关的逻辑电路 示例电路

info::操作说明 鼠标单击开关切换开合状态 A 能使灯点亮并保持; B 则点亮的灯熄灭. 注: 此处使用的是 按钮开关, 松开鼠标后开关会自己断开, 类似于手机和电脑上的电源按钮 因系统原因, 此类开关与普通开关在外观上并无差别. primary::在线交互操作链接 https://cc.xiaogd.net/…

【Android】获取备案所需的公钥以及签名MD5值

目录 重要前提 获取签名MD5值 获取公钥 重要前提 生成jks文件以及gradle配置应用该文件。具体步骤请参考我这篇文章&#xff1a;【Android】配置Gradle打包apk的环境_generate signed bundle or apk-CSDN博客 你只需要从头看到该文章的配置build.gradle&#xff08;app&…

HTML流光爱心

文章目录 序号目录1HTML满屏跳动的爱心&#xff08;可写字&#xff09;2HTML五彩缤纷的爱心3HTML满屏漂浮爱心4HTML情人节快乐5HTML蓝色爱心射线6HTML跳动的爱心&#xff08;简易版&#xff09;7HTML粒子爱心8HTML蓝色动态爱心9HTML跳动的爱心&#xff08;双心版&#xff09;1…

回归预测 | Matlab基于POA-SVR鹈鹕算法优化支持向量机的数据多输入单输出回归预测

回归预测 | Matlab基于POA-SVR鹈鹕算法优化支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于POA-SVR鹈鹕算法优化支持向量机的数据多输入单输出回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab基于POA-SVR鹈鹕算法优化支持向量机的数据…

检查jar冲突,查找存在相同class的jar

写在前面 本文看下如何查找jar冲突&#xff0c;即查找哪些jar包中存在相同的class。如果是存在相同jar的不同版本&#xff0c;基本一眼就能看出来&#xff0c;然后结合maven的依赖关系将其剔除掉即可&#xff0c;但是当你遇到了有人手动拷贝某些class到jar包中导致冲突的情况时…

wpf实现新用户页面引导

第一步 第二部 部分代码: private void show(int xh, FrameworkElement fe, string con, Visibility vis Visibility.Visible) {Point point fe.TransformToAncestor(Window.GetWindow(fe)).Transform(new Point(0, 0));//获取控件坐标点RectangleGeometry rg new Rectangl…

FP7209: 用于紫外线消毒灯的 升压LED恒流驱动芯片

现在社会对于居家消毒也越发重视起来。而居家消毒除了75%浓度酒精及各类消毒液外&#xff0c;利用紫外线灯给衣物表面、房间消毒也是一种很好的选择。FP7209 定位于低压线性恒流驱动&#xff0c;精度高、外围电路简单、使用方便且可靠性高&#xff0c;更可广泛应用于商业照明系…

鸿蒙harmonyos next flutter通信之BasicMessageChannel获取app版本号

本文将通过BasicMessageChannel获取app版本号&#xff0c;以此来演练BasicMessageChannel用法。 建立channel flutter代码&#xff1a; //建立通道 BasicMessageChannel basicMessageChannel BasicMessageChannel("com.xmg.basicMessageChannel",StringCodec());…

STM32自动下载电路分享及注意事项

文章目录 简介ISP下载启动配置 USB转串口芯片CH340C手动isp下载自动isp下载RTS、DTR电平变化分析注意事项 简介 在嵌入式开发中&#xff0c;使用STM32下载程序&#xff0c;可以通过仿真器下载&#xff0c;也可以通过串口下载。在stm32串口下载时&#xff0c;我们需要手动配置启…

【IPv6】IPv6地址格式及地址分类(组播、单播、任播)整理

IPv6地址格式 IPv6 地址从 IPv4 地址的 32 bits 扩展到 128 bits&#xff0c;IPv6 地址的表示、书写方式也从 IPv4 的点分十进制&#xff0c;修改16进制的冒号分割 IPv4 点分格式(.) 192.168.11.11 IPv6 冒号分割(:) 2408:8459:3032:0000:0000:0000:0001:a9fd IPv6 的规范…

Axure大屏可视化模板在不同领域中的实际应用案例

一、农业领域 案例背景&#xff1a; 智慧农业是当前农业发展的重要趋势&#xff0c;通过物联网、大数据等技术手段&#xff0c;实现农业生产的智能化管理。Axure大屏可视化模板在智慧农业平台的建设中发挥了重要作用。 实际应用&#xff1a; 农田环境监控&#xff1a;通过Axu…

828华为云征文 | 华为云Flexus云服务器X实例搭建企业内部VPN私有隧道,以实现安全远程办公

VPN虚拟专用网络适用于企业内部人员流动频繁和远程办公的情况&#xff0c;出差员工或在家办公的员工利用当地ISP就可以和企业的VPN网关建立私有的隧道连接。 通过拨入当地的ISP进入Internet再连接企业的VPN网关&#xff0c;在用户和VPN网关之间建立一个安全的“隧道”&#xff…

智慧环保大数据平台建设方案

1. 智慧环保现状与挑战 随着环境问题日益严重&#xff0c;环境事件频发&#xff0c;如贵州都匀矿渣污染、云南南盘江水污染等&#xff0c;以及癌症高发率的出现&#xff0c;智慧环保建设显得尤为重要。智慧环保旨在通过技术手段提升环境管理和决策的智能化水平。 2. 宏观环境…

OpenCV Canny()函数

OpenCV Canny()函数被用来检测图像物体的边缘。其算法原理如下&#xff1a; 高斯滤波&#xff1a;使用高斯滤波器平滑图像以减少噪声。高斯滤波器是一种线性滤波器&#xff0c;可以消除图像中的高频噪声&#xff0c;同时保留边缘信息。计算梯度强度和方向&#xff1a;使用Sobe…

MySQL高阶2010-职员招聘人数2

目录 题目 准备数据 分析数据 总结 题目 一家公司想雇佣新员工。公司的工资预算是 $70000 。公司的招聘标准是&#xff1a; 继续雇佣薪水最低的高级职员&#xff0c;直到你不能再雇佣更多的高级职员。用剩下的预算雇佣薪水最低的初级职员。继续以最低的工资雇佣初级职员&…