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

yizhihongxing

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#语言程序构建基块的详细讲解攻略。 1. 前置知识 在学习C#语言程序构建基块之前,需要先掌握以下基础知识: C#语言基础语法 常用数据类型和变量定义 控制流语句和循环语句 函数和方法 面向对象编程基础概念 如果你还没有掌握以上基础知识,建议先学习C#语言基础课程。 2. 程序构建基块 程序构建基块,也称为程序库,是指封装了特定功能的代码模块,可…

    C# 2023年5月15日
    00
  • ASP.NET Core开发环境安装配置

    ASP.NET Core是一个跨平台的开源Web框架,用于构建现代化的Web应用程序。在本攻略中,我们将详细讲解如何安装和配置ASP.NET Core开发环境。 安装.NET Core SDK:首先,我们需要安装.NET Core SDK。我们可以从Microsoft官网下载并安装.NET Core SDK。安装完成后,我们可以在命令行中运行dotnet -…

    C# 2023年5月16日
    00
  • .NET使用Collections.Pooled提升性能优化的方法

    .NET使用Collections.Pooled提升性能优化的方法 简述 在进行 .NET 开发过程中,尤其在一些高并发、大量数据操作的场景下,很容易出现内存泄漏和性能问题。而使用 C# 中的 Collections.Pooled 可以有效地缓解此类问题,从而提高程序的性能。本文将详细介绍 Collections.Pooled 的使用方法及优化效果。 Col…

    C# 2023年6月3日
    00
  • 在win7中chm打不开的多种解决方法

    当在win7中尝试打开CHM文件时,有时会遇到无法打开的情况。这种情况一般由于系统权限和安全策略等问题所致。下面是几种解决办法: 方法一:修改注册表 打开注册表编辑器(regedit)。 找到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\HTMLHelp\1.x\ItssRestrictions键值。 如果该键值不存在,则需要手…

    C# 2023年6月7日
    00
  • C#语法相比其它语言比较独特的地方(三)

    下面来详细讲解“C#语法相比其它语言比较独特的地方(三)”。 1. Lambda表达式 Lambda表达式是C#语法中相比其它语言比较独特的地方之一。Lambda表达式是一种匿名函数,可以使用简短、清晰的语法编写。它主要用于简化代码,使代码更加易读。以下是一个简单的Lambda表达式的示例: // 普通方式定义函数 delegate int Calculat…

    C# 2023年6月7日
    00
  • 如何判断 .NET Core 应用程序以管理员身份运行的

    如何判断 .NET Core 应用程序以管理员身份运行 在本攻略中,我们将深入讲解如何判断 .NET Core 应用程序以管理员身份运行,并提供两个示例说明。 判断 .NET Core 应用程序以管理员身份运行的方法 以下是判断 .NET Core 应用程序以管理员身份运行的方法: 使用WindowsIdentity.GetCurrent方法获取当前用户的W…

    C# 2023年5月17日
    00
  • c# 实现MD5,SHA1,SHA256,SHA512等常用加密算法源代码

    c# 实现MD5,SHA1,SHA256,SHA512等常用加密算法源代码 在c#中实现常用的加密算法可以使用System.Security.Cryptography命名空间。 MD5哈希算法 以下是使用MD5哈希算法的c#代码示例: using System.Security.Cryptography; using System.Text; public …

    C# 2023年6月8日
    00
  • C#实现线程池的简单示例

    下面是呈现 C#实现线程池的简单示例 的攻略。 一、什么是线程池 线程池是一种应对多线程编程,用于优化性能的一种技术。简而言之,线程池就是提前创建一堆线程,放入一个池子中,等待其他任务出现时就可以利用这些线程来处理它们,从而避免了每次出现任务时都需要创建新线程的开销。 二、.NET官方提供的线程池 .NET官方提供了ThreadPool类,该类提供了一个全局…

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