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#实现系统托盘通知的方法 在Windows应用程序中,系统托盘是一个很重要的交互方式,在不影响用户正常工作的情况下,可以及时方便的向用户提供各种需要处理和展示的数据。C#提供了丰富的API,帮助我们实现系统托盘通知,本文将介绍两种常见的实现方法。 方法一:使用NotifyIcon类实现 NotifyIcon类为我们提供了丰富的事件和属性,使得我们的系统托盘…

    C# 2023年6月7日
    00
  • asp.net中水印的具体实现代码

    实现 ASP.NET 中水印的具体步骤如下: 步骤1:在页面中引用 JavaScript 和 CSS 文件 首先,在页面头部引用以下两个文件: <link rel="stylesheet" type="text/css" href="watermark.css" /> <scrip…

    C# 2023年5月31日
    00
  • .NET 实现启动时重定向程序运行路径及 Windows 服务运行模式部署的方法

    以下是“.NET实现启动时重定向程序运行路径及Windows服务运行模式部署的方法”的完整攻略: 什么是“.NET实现启动时重定向程序运行路径及Windows服务运行模式部署的方法” “.NET实现启动时重定向程序运行路径及Windows服务运行模式部署的方法”是一种机制,帮助开发人员在.NET应用程序中实现启动时重定向程序运行路径,并在Windows服务运…

    C# 2023年5月12日
    00
  • .NET Core 依赖注入

    在.NET Core中,依赖注入(Dependency Injection,简称DI)是框架的一个重要特性,可以帮助我们解耦代码、管理对象生命周期以及提高可测试性等.一下是.NET Core中依赖注入的一些基本概念和用法:   1、服务(Service):在DI中,服务即一个对象或者类型。用于完成特定的功能.例如,数据库访问、日志记录等都可以看作一个服务  …

    C# 2023年4月27日
    00
  • 使用ajax局部刷新gridview进行数据绑定示例

    下面是使用ajax局部刷新gridview进行数据绑定的完整攻略,包含两条示例说明。 什么是ajax局部刷新 ajax(Asynchronous JavaScript and XML)是一种无需重新加载整个页面而能够更新其中某个局部内容的Web开发技术。当数据发生变化时,ajax技术可以局部刷新响应部分,而不用重新加载整个页面,从而提高用户体验。 如何使用a…

    C# 2023年6月8日
    00
  • c#字符串去掉空格的二种方法(去掉两端空格)

    当我们用C#编写程序时,经常需要对字符串进行处理,比如去掉字符串两端的空格。下面分别介绍两种方法: 方法一:使用Trim()函数 C#的String类内置了Trim()函数,可以用来去掉字符串两端的空格。使用方法非常简单,只需要在需要处理的字符串后面添加.Trim()即可。 string str = " Hello World! "; s…

    C# 2023年6月8日
    00
  • C#实现屏幕拷贝的方法

    若想在C#应用程序中实现屏幕拷贝功能,需要涉及到以下几个步骤: 1. 引用相关命名空间 使用屏幕拷贝功能需要使用System.Drawing和System.Windows.Forms命名空间中的类,需要确保它们被引用。 using System.Drawing; using System.Drawing.Imaging; using System.Windo…

    C# 2023年6月6日
    00
  • 使用typescript改造koa开发框架的实现

    使用Typescript改造Koa开发框架的实现可以带来更好的可维护性和可读性。下面是一些完整的攻略: 步骤一:安装依赖 全局安装TypeScript npm install -g typescript 安装所需的依赖 npm install –save-dev @types/koa @types/node koa koa-router typescrip…

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