码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 事件总线EventBus


    事件总线是对发布-订阅模式的一种实现,是一种集中式事件处理机制,允许不同的组件之间进行彼此通信而又不需要相互依赖,达到一种解耦的目的。

    什么是“总线”:一个集中式的事件处理机制。同时服务多个事件和多个观察者。相当于一个介于Publisher和Subscriber中间的桥梁。它隔离了Publlisher和Subscriber之间的直接依赖,接管了所有事件的发布和订阅逻辑,并负责事件的中转。

    (图片源自:事件总线知多少(1) - 简书) 


    实现方式:

    1,EventBus维护一个事件类型的字典,发布者、订阅者在事件总线中获取此字典并执行发布、订阅操作(维护一个事件源与事件处理的映射字典)。
    2,通过单例模式,确保事件总线的唯一入口
    3,提供统一的事件注册、取消注册和触发(发布)方法

    代码实现:

    发布,订阅都是基于事件。首先我们定义事件接口,泛型接口(规定了事件参数类型)和具体的事件。

    1. ///
    2. /// 事件源
    3. /// 所有被订阅的事件都要继承这个
    4. ///
    5. public abstract class IEvent
    6. {
    7. ///
    8. ///
    9. ///
    10. public string EventName;
    11. }
    12. public abstract class IEvent<T>: IEvent where T : MyEventArgs
    13. {
    14. }
    15. public class EventA :IEvent<MyEventArgsA>
    16. {
    17. }
    18. public class EventB : IEvent<MyEventArgsB>
    19. {
    20. }

    事件参数

    1. public class MyEventArgs:EventArgs
    2. {
    3. ///
    4. /// 事件发生的时间
    5. ///
    6. public DateTime DateTime { get; set; }
    7. }
    8. ///
    9. /// 事件A的事件参数
    10. ///
    11. public class MyEventArgsA:MyEventArgs
    12. {
    13. }
    14. ///
    15. /// 事件B的事件参数
    16. ///
    17. public class MyEventArgsB:MyEventArgs
    18. {
    19. }

    发布者:

    不一定要特别定义一个Publisher。任何人任何时候都可以发布一个事件。只要发生事件并且发布到事件总线中即可。EventB就没有发布者,直接在program中发布 。

    1. public interface IPublisher<T> where T:IEvent
    2. {
    3. ///
    4. /// 发布事件
    5. ///
    6. void Pub();
    7. }
    8. ///
    9. /// 发布者
    10. ///
    11. ///
    12. public class PublisherA : IPublisher<EventA>
    13. {
    14. ///
    15. /// 发布事件
    16. ///
    17. public void Pub()
    18. {
    19. //创建事件并发布
    20. EventBus.Default.Publish(new EventA() { EventName = "我是EventA" }
    21. , this
    22. , new MyEventArgsA() { DateTime = DateTime.Now });
    23. }
    24. }

     订阅者:

    1. ///
    2. /// 观察者(订阅者)接口
    3. /// 规范了订阅的事件类型
    4. ///
    5. public abstract class ISubscriber<IEvent>
    6. {
    7. public abstract void Subscribe(Action<object, MyEventArgs> eventHandler);
    8. }
    9. public class SubscriberA : ISubscriber<EventA>
    10. {
    11. ///
    12. /// 订阅者应该知道自己要执行什么。当然也可以从外部修改
    13. ///
    14. public Action<object, MyEventArgs> eventHandler = (sender, eventArgs) =>
    15. {
    16. Console.WriteLine("ScbscriberA Handler sender:" + sender.ToString() + " eventArgs DateTime:" + eventArgs.DateTime.ToString());
    17. };
    18. public void Subscribe()
    19. {
    20. EventBus.Default.Subscribe(eventHandler);
    21. }
    22. public override void Subscribe(Action<object, MyEventArgs> eventHandler)
    23. {
    24. EventBus.Default.Subscribe(eventHandler);
    25. }
    26. }
    27. public class SubscriberB : ISubscriber<EventB>
    28. {
    29. ///
    30. /// 订阅者应该知道自己要执行什么。所以这里直接定义了一个Action.
    31. /// 当然也可以从外部传入Action
    32. ///
    33. public Action<object, MyEventArgs> eventHandler = (sender, eventArgs) =>
    34. {
    35. Console.WriteLine("ScbscriberB Handler sender:" + sender.ToString() + " eventArgs DateTime:" + eventArgs.DateTime.ToString());
    36. };
    37. public void Subscribe()
    38. {
    39. EventBus.Default.Subscribe(eventHandler);
    40. }
    41. public override void Subscribe(Action<object, MyEventArgs> eventHandler)
    42. {
    43. EventBus.Default.Subscribe(eventHandler);
    44. }
    45. }

    终于到事件总线了。

    1. public class EventBus
    2. {
    3. private static EventBus _default;
    4. protected static readonly object locker = new object();
    5. private Dictionaryobject, MyEventArgs>>> eventDic = new Dictionaryobject, MyEventArgs>>>();
    6. public static EventBus Default
    7. {
    8. get
    9. {
    10. if (_default == null)
    11. {
    12. lock (locker)
    13. {
    14. // 如果类的实例不存在则创建,否则直接返回
    15. if (_default == null)
    16. {
    17. _default = new EventBus();
    18. }
    19. }
    20. }
    21. return _default;
    22. }
    23. }
    24. ///
    25. ///
    26. ///
    27. /// 订阅的时候,就应该知道订阅什么事件
    28. ///
    29. public void Subscribe<T>( Action<object, MyEventArgs> eventHandler) where T:IEvent
    30. {
    31. lock (locker)
    32. {
    33. if (eventDic.ContainsKey(typeof(T)))
    34. {
    35. eventDic[typeof(T)].Add(eventHandler);
    36. }
    37. else
    38. {
    39. eventDic.Add(typeof(T), new Listobject, MyEventArgs>>() { eventHandler });
    40. }
    41. }
    42. }
    43. public void Unsubscribe<T>(Action<object, MyEventArgs> eventHandler) where T : IEvent
    44. {
    45. lock (locker)
    46. {
    47. if (eventDic.ContainsKey(typeof(T)) && eventDic[typeof(T)].Contains(eventHandler))
    48. {
    49. eventDic[typeof(T)].Remove(eventHandler);
    50. }
    51. }
    52. }
    53. ///
    54. /// 发布
    55. ///
    56. /// 发布的时候,应该是发布具体的事件,而不是事件类型
    57. ///
    58. ///
    59. public virtual void Publish(IEvent e,object sender, MyEventArgs eventArgs)
    60. {
    61. lock (locker)
    62. {
    63. if (eventDic.ContainsKey(e.GetType()))
    64. {
    65. var subscriptions = eventDic[e.GetType()];
    66. for (int i = 0; i < subscriptions.Count; i++)
    67. {
    68. subscriptions[i](sender, eventArgs);
    69. }
    70. }
    71. }
    72. }
    73. }

    eventDic就是上面说的维护的事件类型字典。记录了每个事件类型对应的要执行的所有Action。

    EventBus全局唯一实例,这里使用Default实例。

    有订阅,取消订阅,发布方法。也有用反射自动实现订阅的。

     

    最后就是调用

    1. class Program
    2. {
    3. static void Main(string[] args)
    4. {
    5. SubscriberA suba = new SubscriberA();
    6. suba.Subscribe();
    7. PublisherA puba = new PublisherA();
    8. puba.Pub();
    9. SubscriberB subb = new SubscriberB();
    10. subb.Subscribe();
    11. //创建并发布事件
    12. EventBus.Default.Publish(new EventB() { EventName = "我是EventB" }
    13. , "Program"
    14. , new MyEventArgsA() { DateTime = DateTime.Now });
    15. }
    16. }

    输出

    ScbscriberA Handler   sender:MyEventBus.PublisherA    eventArgs DateTime:2022/11/19 10:01:00
    ScbscriberB Handler   sender:Program    eventArgs DateTime:2022/11/19 10:01:00

    比较好的讲事件总线的文章

    什么是事件总线 - 走看看

     详解c# 事件总线

    过程中遇到一个问题:

    类型M继承于N,但是Action无法转换为Action。

    原因如下
    例如
            Action a = (x) => {   
                Console.WriteLine(x.Length);
            };

            Action b = a;//假如Action可以转换为Action。

            b(new object())//无法执行,object无Length属性

  • 相关阅读:
    【二叉树系列】插入&删除&修剪&有序数组转二叉搜索树&二叉搜索树转换为累加树
    基于订单流工具,我们能看到什么?
    20220802NOI模拟赛--考后总结
    C# CodeFormer 图像修复
    React 中利用解构语法 ... 快速方便传递 props 参数
    数据采集-“消防知识网上答题挑战赛”题库
    Java基础面试题
    纽扣电池/含纽扣电池产品上架亚马逊各国法规标准要求16 CFR 第 1700.15/20 ANSI C18.3M(瑞西法案认证)
    Go语言之JSON使用
    吐血整理,Jmeter服务端性能测试-线程阻塞问题案例分析(超细)
  • 原文地址:https://blog.csdn.net/yangguosheng/article/details/127932509
    • 最新文章
    • 攻防演习之三天拿下官网站群
      数据安全治理学习——前期安全规划和安全管理体系建设
      企业安全 | 企业内一次钓鱼演练准备过程
      内网渗透测试 | Kerberos协议及其部分攻击手法
      0day的产生 | 不懂代码的"代码审计"
      安装scrcpy-client模块av模块异常,环境问题解决方案
      leetcode hot100【LeetCode 279. 完全平方数】java实现
      OpenWrt下安装Mosquitto
      AnatoMask论文汇总
      【AI日记】24.11.01 LangChain、openai api和github copilot
    • 热门文章
    • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
      奉劝各位学弟学妹们,该打造你的技术影响力了!
      五年了,我在 CSDN 的两个一百万。
      Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
      面试官都震惊,你这网络基础可以啊!
      你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
      心情不好的时候,用 Python 画棵樱花树送给自己吧
      通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
      13 万字 C 语言从入门到精通保姆级教程2021 年版
      10行代码集2000张美女图,Python爬虫120例,再上征途
    Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
    正则表达式工具 cron表达式工具 密码生成工具

    京公网安备 11010502049817号