首页 > 其他分享 >12.NET观察者模式,发布与订阅

12.NET观察者模式,发布与订阅

时间:2024-02-27 11:35:56浏览次数:26  
标签:订阅 12 Console void 观察者 EventData NET data public

定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。 ——发布订阅模式

发布订阅模式主要有两个角色: 1.发布方(Publisher):也称为被观察者,当状态改变时负责通知所有订阅者。 2.订阅方(Subscriber):也称为观察者,订阅事件并对接收到的事件进行处理。 发布订阅模式有两种实现方式: 1.简单的实现方式:由Publisher维护一个订阅者列表,当状态改变时循环遍历列表通知订阅者。 2.委托的实现方式:由Publisher定义事件委托,Subscriber实现委托。

 

简单地实现发布订阅案例
public class Dog
{
    public void Roar()
    {
        Console.WriteLine("汪汪汪!再不走咬你了");
    }
}
public class Person
{
    public void Roar()
    {
        Console.WriteLine("小偷,哪里跑");
    }
}

 

发布:

/// <summary>
/// 小偷
/// </summary>
public class Thief
{
    public delegate void StealDelegate();//委托
    /// <summary>
    /// 偷盗事件
    /// </summary>
    public event StealDelegate StealEvent;//事件

    /// <summary>
    /// 偷
    /// </summary>
    public void Steal()
    {
        Console.WriteLine("哇,好多钱呀");
        StealEvent.Invoke(); // 触发事件,发布
    }
}

订阅:

Thief thief = new();

// 狗和主人,同时订阅偷盗,并观察有没有小偷进来
thief.StealEvent+= new Dog().Roar;
thief.StealEvent+=new Person().Roar;

// 开始偷盗
thief.Steal();

Console.ReadKey();

 

  如何实现自动订阅发布 发布订阅模式下,无非就涉及到三种实体: 1.事件发布者(EventBus) 2.事件处理者/订阅者(EventHandler) 3.事件源(EventData, 用于传参)   事件源,参数:
/// <summary>
/// 事件源
/// </summary>
public class EventData
{
    /// <summary>
    /// 事件源
    /// </summary>
    public object? Sender { get; set; }

    /// <summary>
    /// 事件发布的时间
    /// </summary>
    public DateTime CreateTime { get; set; } = DateTime.Now;
}

 

订阅接口:

/// <summary>
/// 事件处理者(所有的订阅者都需要继承只接口)
/// 还有一个作用:凡是继承了这个接口的人,都是订阅者
/// </summary>
public interface IEventHandler<in TEventData> where TEventData:EventData
{
    /// <summary>
    /// 事件处理的方法
    /// </summary>
    void Handler(TEventData data);
}

 

发布接口:

/// <summary>
/// 事件发布者
/// </summary>
public interface IEventBus<in TEventData> where TEventData:EventData
{
    void Publish(TEventData data);
}

 

实现IEventBus

/// <summary>
/// 事件总线实现者(所有的发布者都用它)
/// 这个类需要实现两个功能:
/// 1. 自动订阅
/// 2. 订阅者需要自动触发
/// </summary>
public class EventBus<TEventData>:IEventBus<TEventData> where TEventData:EventData
{
    public delegate void EventBusHandler(TEventData data);
    public event EventBusHandler EventBusEvent;

    /// <summary>
    /// 每种EventData 可能对应一组EventHandler(订阅者)
    /// </summary>
    private static readonly Dictionary<Type, List<Type>> EventHandlerMapping = new();
    
    static EventBus()
    {
        // 获取所有实现了IEventHandler<>接口的子类
        var list = (from type in Assembly.GetExecutingAssembly().GetTypes()
            from intf in type.GetInterfaces()
            where intf.IsGenericType && intf.GetGenericTypeDefinition() == typeof(IEventHandler<>)
            select type).ToList();
        foreach (var t in list)
        {
            //获取该类实现的泛型接口
            Type? handlerInterface = t.GetInterface("IEventHandler`1"); 
            if (handlerInterface != null)
            {
                // 获取泛型接口指定的EventData参数类型
                Type eventDataType = handlerInterface.GetGenericArguments()[0]; 
                if (EventHandlerMapping.ContainsKey(eventDataType))
                {
                    List<Type> handlerTypes = EventHandlerMapping[eventDataType];
                    handlerTypes.Add(t);
                    EventHandlerMapping[eventDataType] = handlerTypes;
                }
                else
                {
                    var handlerTypes = new List<Type> {t};
                    EventHandlerMapping[eventDataType] = handlerTypes;
                }
            }
        }

    }
    
    /// <summary>
    /// 发布事件, 订阅者自动触发
    /// </summary>
    /// <param name="data"></param>
    /// <exception cref="NotImplementedException"></exception>
    public void Publish(TEventData data)
    {
        var eventHandlerList = EventHandlerMapping[data.GetType()];
        foreach (var t in eventHandlerList)
        {
            // 自动订阅事件
            EventBusEvent +=  (Activator.CreateInstance(t) as IEventHandler<TEventData>)!.Handler;
        }
        // 触发事件
        EventBusEvent?.Invoke(data);
    }
}

 

案列1,无参订阅

public class Dog:IEventHandler<EventData>
{
    public void Handler(EventData data)
    {
        Console.WriteLine("汪汪汪,再不走就咬你");
    }
}
public class Person:IEventHandler<EventData>
{
    public void Handler(EventData data)
    {
        Console.WriteLine("小偷哪里逃");
    }
}

实现:

Console.WriteLine("Hello, World!");
// 事件源
EventData data = new();
// 创建发布者
IEventBus<EventData> bus = new EventBus<EventData>();
// 发布事件
bus.Publish(data);

 

案例2,有参订阅:

参数类

/// <summary>
/// 消息通知 事件参数
/// </summary>
public class NotifyEventData:EventData
{
    /// <summary>
    /// 通知接收者
    /// </summary>
    public string Receiver { get; set; } = null!;
}

 

订阅,发送Email

public class EmailEventHandler:IEventHandler<NotifyEventData>
{
    public void Handler(NotifyEventData data)
    {
        Console.WriteLine($"{data.Receiver} 收到了邮件通知");
    }
}

 

订阅,发送短信

public class SmsEventHandler:IEventHandler<NotifyEventData>
{
    public void Handler(NotifyEventData data)
    {
        Console.WriteLine($"{data.Receiver} 收到了短信通知");
    }
}

 

发布

Console.WriteLine("Hello, World!");

IEventBus<NotifyEventData> bus2 = new EventBus<NotifyEventData>();
bus2.Publish(new NotifyEventData {Receiver = "张三"});

 

标签:订阅,12,Console,void,观察者,EventData,NET,data,public
From: https://www.cnblogs.com/MingQiu/p/18036516

相关文章

  • Advanced .Net Debugging 2:CLR基础
    一、简介这是2024新年后我的第一篇文章,也是我的《Advanced.NetDebugging》这个系列的第二篇文章。这篇文章告诉我们为了进行有效的程序调试,我们需要掌握哪些知识。言归正传,无论采取什么形式来分析问题,对被调试系统的底层了解的越多,就越有可能成功的找出问题的根源。在N......
  • 开发框架DevExpress XAF - Entity Framework Core 8支持.NET 8性能基准
    DevExpressXAF是一款强大的现代应用程序框架,允许同时开发ASP.NET和WinForms。XAF采用模块化设计,开发人员可以选择内建模块,也可以自行创建,从而以更快的速度和比开发人员当前更强有力的方式创建应用程序。对于使用EntityFrameworkCore(EFCore)(实体核心框架)的用户来说,这是一个......
  • P1240+P1350+ AT_abc282_g得出的一些dp技巧
    在遇到一些题目在设状态时,前面的状态对后面的有影响,比如在P1240和P1350中前面的放置会对后面的有影响,正常的状态是不行的。以前可能考虑状态压缩,但现在我们可以通过发现前面状态的一些共性,比如在P1240+P1350中前面放了k个車那么一定有k行被占用,所以就不用记录之前那些行被占用了。......
  • .net+bootstrap写的一个还不错的音乐网站
    以前做的一款设计音乐网站,分享下。技术用的是.net+sqlserver  大致的样子是这样的。1、首页如下: 2、播放歌词页面如下:歌词自动滚动,且可悬停。 3、歌单信息页面如下:详细页面如下:可删除歌单中歌曲信息。 编辑页面如下:可进行编辑封面和歌单名、简介等。 个人信息......
  • .NET周刊【2月第3期 2024-02-25】
    国内文章4.1kStar!全面的C#/.NET/.NETCore学习、工作、面试指南https://www.cnblogs.com/Can-daydayup/p/18027117DotNetGuide是一个为.NET开发者建立的技术社区和知识库。其中包含.NET相关的学习资料、工作心得、面试指南、技术文章、项目框架和常见面试题等,目的是帮助初学者......
  • 12. 整数转罗马数字(中)
    目录题目题解题目罗马数字包含以下七种字符:I,V,X,L,C,D和M。字符数值I1V5X10L50C100D500M1000例如,罗马数字2写做II,即为两个并列的1。12写做XII,即......
  • 89th 2024/1/12 GDKOI
    题外话新一年的第一篇总结拖到了现在快到GDKOI的时候,忽然brokeout了一场流感所以被感染后影响了接下来一系列事情只想说身体是学习的基础,要保持好健康GDKOI挺失败的,也可能是因为带低烧去打的原因脑袋有点混混沌沌的,想不清东西,赛后发现一些失误后追悔莫及Day1的题目难度较......
  • [DotnetSec]XmlSerializer 反序列化 分析
    Dotnet-XmlSerializer反序列化序列化和反序列化的演示Demo参考微软的文档:https://learn.microsoft.com/zh-cn/dotnet/api/system.xml.serialization.xmlserializer?view=net-5.0XmlSerializer命名空间:System.Xml.Serialization程序集:System.Xml.XmlSerializer.dll演示......
  • STEP: 用于多变量时间序列预测的预训练增强时空图神经网络《Pre-training Enhanced Sp
    2023年12月27日,看一篇老师给的论文。论文:Pre-trainingEnhancedSpatial-temporalGraphNeuralNetworkforMultivariateTimeSeriesForecasting或者是:Pre-trainingEnhancedSpatial-temporalGraphNeuralNetworkforMultivariateTimeSeriesForecastingGitHub:https:......
  • .NET高级调试之sos命令输出看不懂怎么办
    一:背景1.讲故事很多.NET开发者在学习高级调试的时候,使用sos的命令输出会发现这里也看不懂那里也看不懂,比如截图中的这位朋友。.NET高级调试属于一个偏冷门的领域,国内可观测的资料比较少,所以很多东西需要你自己去探究源代码,然后用各种调试工具去验证,相关源代码如下:coreclr:......