C#的Event事件示例小白级剖析

1、委托Delegate

首先说一下delegate委托,委托是将方法作为参数进行传递。

// 定义了一个委托类型

public delegate void MyDelegate(int num);

// 定义了一个啥也不干的委托实例

public MyDelegate m_delegate = _ => {};

// 定义了一个和委托相同格式的方法

public void MyFun(int num){

        Console.WriteLine(num);

};  

// 添加新的委托

m_delegate += MyFun;

// 执行任务

m_delegate(2);

2、基于委托的发布/订阅模式

基于上例,对于如下的操作:

m_delegate += MyFun1;

m_delegate += MyFun2;

....

m_delegate(1);

这是一个委托的多播操作,一连串的任务会一起执行。

此时换一个思维,假如把+=操作看成是订阅操作。m_delegate(1)是发布,1可以看做是一个message,可以是int,也可以是string。这样就构成了一个发布/订阅模式。

当m_delegate(1)发布执行时,委托的多播会让所有订阅函数执行。

所以订阅者只需要把委托格式的函数告诉发布者(+=注册进去),当发布者发布时,所有的订阅者就相当于收到了消息。

下面写一个基于委托的发布/订阅模式:

#define DEBUG3

using System;
using System.Diagnostics;

namespace ConsoleApp1
{
    public delegate void SayGate(int num);

    public class Publisher
    {
        // 定义委托
        public SayGate SayGateHandler;

        // 触发任务
        public void OnTrigger(int num)
        {
            // 触发任务
            SayGateHandler(num);
        }
    }

    public class Subscriber
    {
        public void Subscribe(Publisher publisher)
        {
            // 订阅
            publisher.SayGateHandler += SayHello;
        }

        public void SayHello(int num)
        {
            Console.WriteLine("subscribe1: " + num);
        }
    }

    public class Subscriber2
    {
        public void Subscribe(Publisher publisher)
        {
            // 订阅
            publisher.SayGateHandler += SayHello;
        }

        public void SayHello(int num)
        {
            Console.WriteLine("subscribe2: " + num);
        }
    }

    internal class Program
    {
        
        static void Main(string[] args) {
            Publisher publisher = new Publisher();

            Subscriber subscriber = new Subscriber();
            subscriber.Subscribe(publisher);
            Subscriber2 subscriber2 = new Subscriber2();
            subscriber2.Subscribe(publisher);

            Console.ReadLine();
            publisher.OnTrigger(1);
            Console.ReadLine();
          
        }
    }
}

执行后会显示:

subscribe1: 1
subscribe2: 1

3、存在的缺陷

假如有个订阅者干了坏事,把"+="写成了"=",比如:

此时执行后会显示:

subscribe2: 1

即只显示了一条。

4、事件Event

针对上面的缺陷,需要引入一种机制限制订阅者的行为。

在C#中,event是一种特殊的委托,特殊之处为:订阅者只能通过"+="和"-="订阅或取消。发布者仍然可以进行"="操作。

当将SayGateHandler定义为event之后,订阅者2"="方式的订阅提示失败。

错误信息为:事件"Publisher.SayGateEvent"只能出现在+=或-=的左边(从类型“Publisher“中使用时除外)

新程序如下:

namespace ConsoleApp1
{
    public delegate void SayGate(int num);

    public class Publisher
    {
        // 定义委托
        public event SayGate SayGateHandler;

        // 触发任务
        public void OnTrigger(int num)
        {
            // 触发任务
            SayGateHandler(num);
        }
    }

    public class Subscriber
    {
        public void Subscribe(Publisher publisher)
        {
            // 订阅
            publisher.SayGateHandler += SayHello;
        }

        public void SayHello(int num)
        {
            Console.WriteLine("subscribe1: " + num);
        }
    }

    public class Subscriber2
    {
        public void Subscribe(Publisher publisher)
        {
            // 订阅
            publisher.SayGateHandler += SayHello;
        }

        public void SayHello(int num)
        {
            Console.WriteLine("subscribe2: " + num);
        }
    }

    internal class Program
    {
        
        static void Main(string[] args) {
            Publisher publisher = new Publisher();

            Subscriber subscriber = new Subscriber();
            subscriber.Subscribe(publisher);
            Subscriber2 subscriber2 = new Subscriber2();
            subscriber2.Subscribe(publisher);

            Console.ReadLine();
            publisher.OnTrigger(1);
            Console.ReadLine();
          
        }
    }
}

5、存在的缺陷2

以上程序有2个订阅者,但是假如没有订阅者,此时发布后会怎么样?

将以上代码注释后做测试,报错信息如下:

此处报错的意思是:SayGameHandler没有实例化。

有两种解决方案:

1)发布者实例化一个空的委托,

public event SayGate SayGateHandler = _ => { };

2)触发事件时检测下是否实例化,

// 触发任务

SayGateHandler?.Invoke(num);

两种方法选择一种即可。

最后一个完整的Event示例如下:

namespace ConsoleApp1
{
    public delegate void SayGate(int num);

    public class Publisher
    {
        // 定义委托
        public event SayGate SayGateHandler;

        // 触发任务
        public void OnTrigger(int num)
        {
            // 触发任务
            SayGateHandler?.Invoke(num);
        }
    }

    public class Subscriber
    {
        public void Subscribe(Publisher publisher)
        {
            // 订阅
            publisher.SayGateHandler += SayHello;
        }

        public void SayHello(int num)
        {
            Console.WriteLine("subscribe1: " + num);
        }
    }

    public class Subscriber2
    {
        public void Subscribe(Publisher publisher)
        {
            // 订阅
            publisher.SayGateHandler += SayHello;
        }

        public void SayHello(int num)
        {
            Console.WriteLine("subscribe2: " + num);
        }
    }

    internal class Program
    {
        
        static void Main(string[] args) {
            Publisher publisher = new Publisher();

            Subscriber subscriber = new Subscriber();
            subscriber.Subscribe(publisher);
            Subscriber2 subscriber2 = new Subscriber2();
            subscriber2.Subscribe(publisher);

            Console.ReadLine();
            publisher.OnTrigger(1);
            Console.ReadLine();
          
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣排序350题 两个元组的交集2

题目: 给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两 数组的交集。返回结果中每个元素出现的次数,应与元素在两个 数组中都出现的次数一致(如果出现次数不一致,则考虑取 较小值)。可以不考虑输出…

善用Git LFS来降低模型文件对磁盘的占用

将讲一个实际的例子:对于模型文件,动辄就是好几个G,而有的仓库更是高达几十G,拉一个仓库到本地,稍不注意直接磁盘拉满都有可能。 比如:meta-llama-3.1-8b-instruct,拉到本地后发现居然占用了60G…

CentOS 磁盘扩容

1. 查看要扩展的磁盘 df -h这个就是要扩展的磁盘空间,记住名称,后面会用到 2. 查看所有磁盘信息 fdisk -llsblk可以发现,500G 的硬盘已经安装到服务器但是没被使用,此时需要操作这块硬盘 3. 创建分区 fdisk /dev/vdb根据流程…

js中怎么把excel和pdf文件转换成图片打包下载

index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>文件转图片工具</title><!-- 本…

【JavaEE初阶 — 多线程】Thread类的方法&线程生命周期

目录 1. start() (1) start() 的性质 (2) start() 和 Thread类 的关系 2. 终止一个线程 (1)通过共享的标记结束线程 1. 通过共享的标记结束线程 2. 关于 lamda 表达式的“变量捕获” (2) 调用interrupt()方法 1. isInterrupted() 2. currentThread() …

Metasploit渗透测试之在云服务器中使用MSF

概述 随着云计算的发展&#xff0c;对基于云的应用程序、服务和基础设施的测试也在不断增加。在对云部署进行渗透测试时&#xff0c;最大的问题之一是共享所有权。过去&#xff0c;在进行渗透测试时&#xff0c;企业会拥有网络上的所有组件&#xff0c;我们可以对它们进行全部…

2016年7月和8月NASA的气候成像(ATom)-1飞行活动期间测量的黑碳(BC)质量混合比(单位为ng BC / kg空气)

目录 简介 摘要 代码 引用 网址推荐 知识星球 机器学习 简介 ATom: Black Carbon Mass Mixing Ratios from ATom-1 Flights 该数据集提供了在2016年7月和8月NASA的气候成像&#xff08;ATom&#xff09;-1飞行活动期间测量的黑碳&#xff08;BC&#xff09;质量混合比&…

关于各链 Meme Launchpad

随着Web3生态的迅猛发展&#xff0c;区块链领域诞生了大量创意无限的meme项目&#xff0c;逐渐引起了广泛关注。这些meme项目不仅展示了加密社区的活力与创造力&#xff0c;也为投资者提供了新的机会和玩法。 然而&#xff0c;meme项目的快速崛起也带来了筛选优质项目和发现市场…

C语言 | Leetcode C语言题解之第537题复数乘法

题目&#xff1a; 题解&#xff1a; bool parseComplexNumber(const char * num, int * real, int * image) {char *token strtok(num, "");*real atoi(token);token strtok(NULL, "i");*image atoi(token);return true; };char * complexNumberMulti…

C# 日志框架 NLog、log4net 和 Serilog对比

文章目录 前言NLog、log4net 和 Serilog 三个框架的详细对比:一、NLog优点:缺点:二、 log4net优点缺点三、Serilog优点缺点四、Serilog使用举例总结前言 NLog、log4net 和 Serilog 三个框架的详细对比: NLog、log4net 和 Serilog 是三个非常流行的 .NET 日志框架,它们各自…

助力风力发电风机设备智能化巡检,基于YOLOv8全系列【n/s/m/l/x】参数模型开发构建无人机巡检场景下风机叶片缺陷问题智能化检测预警模型

在全球能源转型的大潮中&#xff0c;清洁环境能源的发展已成为各国关注的焦点。风力发电作为其中的佼佼者&#xff0c;以其可再生、无污染的特点&#xff0c;受到了广泛的青睐。然而&#xff0c;风力发电设施大多建于人迹罕至的地区&#xff0c;设备庞大且复杂&#xff0c;其维…

RV1126-SDK学习之OSD实现原理

RV1126-SDK学习之OSD实现原理 前言 本文简单记录一下我在学习RV1126的SDK当中OSD绘制的原理的过程。 在学习OSD的过程当中&#xff0c;可能需要补充的基础知识&#xff1a; OSD是什么&#xff1f; BMP图像文件格式大致组成&#xff1f; 图像调色&#xff08;Palette&#…

BFV/BGV全同态加密方案浅析

本文主要为翻译内容&#xff0c;原文地址&#xff1a;Introduction to the BFV encryption scheme、https://www.inferati.com/blog/fhe-schemes-bgv 之前的一篇博客我们翻译了CKKS全同态加密方案的内容&#xff0c;但该篇上下文中有一些知识要点&#xff0c;作者在BFV/BGV中已…

前端小练习——星辰宇宙(JS没有上限!!!)

前言&#xff1a;在刚开始学习前端的时候&#xff0c;我们会学习到前端三件套中的JavaScript&#xff0c;可能那时候读者没有觉得JavaScript这个语言有多么的牛逼&#xff0c;本篇文章将会使用一个炫酷的案例来刷新你对JavaScript这个语言的认知与理解。 ✨✨✨这里是秋刀鱼不做…

JavaScript 生成二维码

我试过了&#xff0c;这一款js库支持中英文混合。 进入网站后&#xff0c;可以直接点击运行哟&#xff5e; https://andi.cn/page/621821.html

Vue全栈开发旅游网项目(6)-接口开发

1.景点详情接口开发 1.设计响应数据结构 文件地址&#xff1a;sight/serializers.py 创建类&#xff1a; class SightDetailSerializers(BaseSerializer):#景点详情def to_dict(self):obj self.objreturn {id: obj.id,name: obj.name,desc: obj.desc,img: obj.banner_img.…

【嵌入式】STM32中的SPI通信

SPI是由摩托罗拉公司开发的一种通用数据总线&#xff0c;其中由四根通信线&#xff0c;支持总线挂载多设备&#xff08;一主多从&#xff09;&#xff0c;是一种同步全双工的协议。主要是实现主控芯片和外挂芯片之间的交流。这样可以使得STM32可以访问并控制各种外部芯片。本文…

微服务系列一:基础拆分实践

目录 前言 一、认识微服务 1.1 单体架构 VS 微服务架构 1.2 微服务的集大成者&#xff1a;SpringCloud 1.3 微服务拆分原则 1.4 微服务拆分方式 二、微服务拆分入门步骤 &#xff1a;以拆分商品模块为例 三、服务注册订阅与远程调用&#xff1a;以拆分购物车为例 3.1 …

密码学简要介绍

密码学是研究编制密码和破译密码的技术科学&#xff0c;它研究密码变化的客观规律&#xff0c;主要包括编码学和破译学两大部分。 一、定义与起源 定义&#xff1a;密码学是研究如何隐密地传递信息的学科&#xff0c;在现代特别指对信息以及其传输的数学性研究&#xff0c;常被…

苄基异喹啉类生物碱的微生物合成-文献精读77

苄基异喹啉类生物碱的微生物合成研究进展及挑战 摘要 微生物发酵是一种经济高效、可持续的生产方式&#xff0c;可替代植物种植和化学合成来生产多种植物来源的药物。苄基异喹啉类生物碱作为植物来源生物碱的典型代表&#xff0c;具有多种重要的生理活性&#xff0c;已成为极具…