C#事件订阅发布实现原理详解

C#事件订阅发布实现原理详解

一、事件订阅发布机制简介

事件是C#中最常用的一种机制之一,它可以将对象之间的通信极大地简化和解耦。订阅和发布是事件发生的关键步骤,其中订阅(或称为注册)表示一个对象准备接收来自另一个对象(即发布者)的通知,而发布(或称为引发)则表示对象触发了一个事件并向订阅该事件的其他对象发送通知。

在C#中,此机制通过event关键字来实现。当一个事件被声明为event时,它本质上是一个特殊的委托,而事件的引发则是通过委托的执行来实现的。因此,订阅者需要将它们的方法添加到事件的委托列表中,而发布者在事件被引发时将向该列表中的每个订阅者发送通知。

二、订阅发布机制的实现原理

事件的订阅和发布实现有多种方式,本文将介绍使用C#中的委托和事件机制实现订阅发布的方法。订阅发布机制的实现步骤如下:

1. 定义事件

在C#中,可以通过event关键字定义事件。例如:

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

public class SomePublisher
{
    public event EventHandler SomeEvent;
}

以上代码定义了一个名为SomeEvent的事件,并将其类型定义为EventHandler,即一个具有两个参数(object类型和EventArgs类型)和无返回值的委托。在SomePublisher类中使用event关键字定义SomeEvent事件,并标识为公共事件,以允许其他类将其订阅。

2. 发布事件

发布(或引发)事件是通过调用类中的事件函数来实现的。例如:

public void DoSomething()
{
    OnSomeEvent(this, EventArgs.Empty);
}

protected virtual void OnSomeEvent(object sender, EventArgs e)
{
    SomeEvent?.Invoke(sender, e);
}

以上代码展示了在SomePublisher类中如何发布事件。在DoSomething方法中调用OnSomeEvent函数,将传递this作为调用者(即发布者),以及一个空的EventArgs参数(也可以使用自定义的事件参数类型)。OnSomeEvent方法是一个虚拟方法,允许派生类覆盖此方法以提供自己的实现。在其默认实现中,该方法使用条件运算符检查SomeEvent是否为空,以避免在无订阅事件的情况下引发空引用异常。如果SomeEvent不为空,则使用委托的Invoke方法向委托列表中的每个订阅者发送通知。

3. 订阅事件

订阅事件是将订阅者的方法添加到事件委托列表中的过程。以下是一个简单的示例:

public class SomeSubscriber
{
    public void SubscribeTo(SomePublisher publisher)
    {
        publisher.SomeEvent += HandleSomeEvent;
    }

    public void UnsubscribeFrom(SomePublisher publisher)
    {
        publisher.SomeEvent -= HandleSomeEvent;
    }

    private void HandleSomeEvent(object sender, EventArgs e)
    {
        Console.WriteLine("Some event occurred.");
    }
}

以上代码展示了一个名为SomeSubscriber的订阅者类,它定义了两个公共方法:SubscribeToUnsubscribeFrom,允许它将自己添加到和从订阅列表中删除。HandleSomeEvent方法是SomeEvent事件的处理函数,它将在事件被引发时执行。

三、示例说明

下面通过两个示例来说明事件订阅发布机制的使用方法。

示例1:使用事件机制实现线程通信

using System;
using System.Threading;

namespace EventDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            var instance = new EventDemo();
            Thread thread = new Thread(delegate() { instance.DoWork(); });
            thread.Start();
            while (true)
            {
                Console.ReadLine();
                instance.Cancel();
            }
        }
    }
    public class EventDemo
    {
        public event EventHandler Done;
        private bool _cancelled;
        public void DoWork()
        {
            int count = 0;
            while (!_cancelled && count < 10)
            {
                count++;
                Console.WriteLine("Working... " + count);
                Thread.Sleep(1000);
            }
            if (!_cancelled)
            {
                OnDone(new EventArgs());
            }
            Console.WriteLine("Work done.");
        }
        protected virtual void OnDone(EventArgs e)
        {
            Done?.Invoke(this, e);
        }
        public void Cancel()
        {
            Console.WriteLine("Cancelling...");
            _cancelled = true;
        }
    }
}

以上示例展示了如何使用事件机制实现线程之间的通信。在EventDemo类中定义了一个名为Done的事件,表示已完成某项任务。在DoWork方法中,执行一些工作,如果_cancelled字段为false,则在工作完成时引发Done事件。为终止工作,我们可以在控制台上按回车键,然后调用Cancel方法来设置_cancelled字段为true,以立即取消操作。

在程序的主线程中,我们使用委托来开启一个新线程,开始执行DoWork方法。之后我们进入一个无限循环,等待在控制台上输入回车,然后结束工作并关闭线程。

示例2:使用事件机制实现对象通信

using System;

namespace EventDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            var publisher = new SomePublisher();
            var subscriber1 = new SomeSubscriber("Subscriber 1");
            var subscriber2 = new SomeSubscriber("Subscriber 2");

            subscriber1.SubscribeTo(publisher);
            subscriber2.SubscribeTo(publisher);

            publisher.DoSomething();

            subscriber1.UnsubscribeFrom(publisher);
            publisher.DoSomething();
        }
    }

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

    public class SomePublisher
    {
        public event EventHandler SomeEvent;

        public void DoSomething()
        {
            OnSomeEvent(this, EventArgs.Empty);
        }

        protected virtual void OnSomeEvent(object sender, EventArgs e)
        {
            SomeEvent?.Invoke(sender, e);
        }
    }

    public class SomeSubscriber
    {
        private string _name;
        public SomeSubscriber(string name)
        {
            _name = name;
        }

        public void SubscribeTo(SomePublisher publisher)
        {
            publisher.SomeEvent += HandleSomeEvent;
        }

        public void UnsubscribeFrom(SomePublisher publisher)
        {
            publisher.SomeEvent -= HandleSomeEvent;
        }

        private void HandleSomeEvent(object sender, EventArgs e)
        {
            Console.WriteLine(_name + " received event.");
        }
    }
}

以上示例展示了如何使用事件机制实现简单的对象通信。在这个例子中,SomePublisher类定义了一个名为SomeEvent的事件,表示某些事件已发生。在DoSomething方法中,当操作完成时,将引发SomeEvent事件。SomeSubscriber类定义了一个构造函数,它允许为Subscriber指定名称,并且提供两个公共方法,SubscribeToUnsubscribeFrom,允许它将自己添加到和从订阅列表中删除。在HandleSomeEvent方法中,订阅者将收到事件并输出消息。

在程序的主线程中,我们创建一个SomePublisher对象和两个SomeSubscriber对象。两个订阅者都将publisher实例的SomeEvent事件添加到其订阅列表中。在调用publisherDoSomething方法时,两个订阅者的HandleSomeEvent方法都将被调用,并且两个订阅者将输出消息。之后,我们取消第一个订阅者,再次调用publisherDoSomething方法,只有第二个订阅者的HandleSomeEvent方法将被调用,输出消息。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#事件订阅发布实现原理详解 - Python技术站

(0)
上一篇 2023年6月6日
下一篇 2023年6月6日

相关文章

  • C#实现简单聊天程序的方法

    C#是一种非常强大的编程语言,可以用来实现各种各样的应用程序,包括聊天程序。下面是实现简单聊天程序的方法: 第一步:创建Socket 在C#中实现聊天程序的第一步是创建Socket。Socket是通信协议的一个抽象概念,它提供了一种可以在网络上发送和接收数据的方法。在C#中,可以使用System.Net.Sockets.Socket类创建Socket。 us…

    C# 2023年6月7日
    00
  • WPF 调用 ECAN 发送数据会阻塞的解决过程

    接了个活, 写个 WPF 上位机用 PCAN 或 ECAN 和单片机通讯, 读取传感器数据. 程序逻辑是 : 选择连接类型 PCAN / ECAN, 选择波特率, 选择通道号, 输入查询间隔, 连接设备. 然后开启一个后台线程循环发送读取指令逐个读取传感器数据. 使用 PCAN 时, 连接和收发数据都正常, 但改为 ECAN 连接后, 有很大几率卡在 SDK…

    C# 2023年5月2日
    00
  • .net core并发请求发送HttpWebRequest的坑解决

    针对“.net core并发请求发送HttpWebRequest的坑解决”这个问题,我们可以进行以下操作: 问题描述 在使用.NET Core进行并发请求发送HttpWebRequest时,会出现一些并发请求异常和内存泄漏等问题。但是究竟是什么原因导致的呢?以下是一些原因的总结: HttpWebRequest与KeepAlive的冲突。 缺少正确的限制请求并…

    C# 2023年6月3日
    00
  • C#新手常犯的错误汇总

    C#新手常犯的错误汇总 前言 C#作为一门流行的编程语言,吸引了很多新手程序员的青睐。但是,在学习和练习过程中,新手程序员常常会犯一些错误。本文将总结并详细讲解C#新手程序员常犯的错误,并提供完整的解决方案。 1. 变量的生命周期不清楚 在C#中,变量的生命周期是很重要的一个概念。如果不清楚变量的生命周期,可能会导致程序出现奇怪的问题。 错误示例 publi…

    C# 2023年5月15日
    00
  • C#微信公众号开发之服务器配置

    C#微信公众号开发之服务器配置 本文主要介绍在使用C#进行微信公众号开发过程中,如何进行服务器配置,以让公众号接收用户消息和事件以及进行回复。下面就是服务器配置的完整攻略: 1. 登录开发者平台 首先,在微信公众平台官网登录自己的开发者账号,然后进入“开发->基本配置”界面,在该界面获取自己的AppID和AppSecret,为后面进行开发提供必要的认证…

    C# 2023年6月6日
    00
  • c# 理解csredis库实现分布式锁的详细流程

    下面是关于实现分布式锁的详细攻略: 1. 简介 在分布式系统中,分布式锁是实现数据安全访问的一种重要手段。常见的分布式锁实现方法有使用Redis实现,在C#中可以使用csredis库来方便地实现分布式锁。 csredis是一个Redis的C#客户端,提供了简单、高性能、高可靠性的封装。在csredis中实现分布式锁需要使用到Redis的原子命令setnx(S…

    C# 2023年6月3日
    00
  • 用Fine Uploader+ASP.NET MVC实现ajax文件上传[代码示例]

    使用Fine Uploader和ASP.NET MVC实现ajax文件上传是一项非常常见的任务。下面是实现这个任务的完整攻略: 步骤一:安装Fine Uploader 首先,需要从Fine Uploader的官方网站下载Fine Uploader。然后,将下载的Fine Uploader文件解压缩到您的应用程序中。 步骤二:设置文件上传 在您的ASP.NET…

    C# 2023年5月31日
    00
  • C# ContainsKey(Object):确定集合是否包含具有指定键的元素

    C# ContainsKey(Object) 方法详解 在C#中,ContainsKey(Object)方法是一个用于Dictionary类中的方法。它可以用于确定指定的键是否存在于字典中,并返回一个布尔值作为结果。在这里,我们将详细讲解ContainsKey(Object)的完整攻略。 语法 public bool ContainsKey(object k…

    C# 2023年4月19日
    00
合作推广
合作推广
分享本页
返回顶部