事件总线是对发布-订阅模式的一种实现,是一种集中式事件处理机制,允许不同的组件之间进行彼此通信而又不需要相互依赖,达到一种解耦的目的。
什么是“总线”:一个集中式的事件处理机制。同时服务多个事件和多个观察者。相当于一个介于Publisher和Subscriber中间的桥梁。它隔离了Publlisher和Subscriber之间的直接依赖,接管了所有事件的发布和订阅逻辑,并负责事件的中转。

(图片源自:事件总线知多少(1) - 简书)
实现方式:
1,EventBus维护一个事件类型的字典,发布者、订阅者在事件总线中获取此字典并执行发布、订阅操作(维护一个事件源与事件处理的映射字典)。
2,通过单例模式,确保事件总线的唯一入口
3,提供统一的事件注册、取消注册和触发(发布)方法
代码实现:
发布,订阅都是基于事件。首先我们定义事件接口,泛型接口(规定了事件参数类型)和具体的事件。
- ///
- /// 事件源
- /// 所有被订阅的事件都要继承这个
- ///
- public abstract class IEvent
- {
- ///
- ///
- ///
- public string EventName;
- }
-
- public abstract class IEvent<T>: IEvent where T : MyEventArgs
- {
- }
-
- public class EventA :IEvent<MyEventArgsA>
- {
- }
-
- public class EventB : IEvent<MyEventArgsB>
- {
- }
事件参数
- public class MyEventArgs:EventArgs
- {
- ///
- /// 事件发生的时间
- ///
- public DateTime DateTime { get; set; }
- }
-
- ///
- /// 事件A的事件参数
- ///
- public class MyEventArgsA:MyEventArgs
- {
-
- }
-
- ///
- /// 事件B的事件参数
- ///
- public class MyEventArgsB:MyEventArgs
- {
-
- }
发布者:
不一定要特别定义一个Publisher。任何人任何时候都可以发布一个事件。只要发生事件并且发布到事件总线中即可。EventB就没有发布者,直接在program中发布 。
- public interface IPublisher<T> where T:IEvent
- {
- ///
- /// 发布事件
- ///
- void Pub();
- }
-
- ///
- /// 发布者
- ///
- ///
- public class PublisherA : IPublisher<EventA>
- {
- ///
- /// 发布事件
- ///
- public void Pub()
- {
- //创建事件并发布
- EventBus.Default.Publish(new EventA() { EventName = "我是EventA" }
- , this
- , new MyEventArgsA() { DateTime = DateTime.Now });
- }
- }
-
订阅者:
- ///
- /// 观察者(订阅者)接口
- /// 规范了订阅的事件类型
- ///
- public abstract class ISubscriber<IEvent>
- {
- public abstract void Subscribe(Action<object, MyEventArgs> eventHandler);
-
- }
-
- public class SubscriberA : ISubscriber<EventA>
- {
- ///
- /// 订阅者应该知道自己要执行什么。当然也可以从外部修改
- ///
- public Action<object, MyEventArgs> eventHandler = (sender, eventArgs) =>
- {
- Console.WriteLine("ScbscriberA Handler sender:" + sender.ToString() + " eventArgs DateTime:" + eventArgs.DateTime.ToString());
- };
-
- public void Subscribe()
- {
- EventBus.Default.Subscribe
(eventHandler); - }
- public override void Subscribe(Action<object, MyEventArgs> eventHandler)
- {
- EventBus.Default.Subscribe
(eventHandler); - }
- }
-
- public class SubscriberB : ISubscriber<EventB>
- {
- ///
- /// 订阅者应该知道自己要执行什么。所以这里直接定义了一个Action.
- /// 当然也可以从外部传入Action
- ///
- public Action<object, MyEventArgs> eventHandler = (sender, eventArgs) =>
- {
- Console.WriteLine("ScbscriberB Handler sender:" + sender.ToString() + " eventArgs DateTime:" + eventArgs.DateTime.ToString());
- };
-
- public void Subscribe()
- {
- EventBus.Default.Subscribe
(eventHandler); - }
- public override void Subscribe(Action<object, MyEventArgs> eventHandler)
- {
- EventBus.Default.Subscribe
(eventHandler); - }
-
- }
终于到事件总线了。
- public class EventBus
- {
- private static EventBus _default;
- protected static readonly object locker = new object();
- private Dictionary
object, MyEventArgs>>> eventDic = new Dictionaryobject, MyEventArgs>>>(); - public static EventBus Default
- {
- get
- {
- if (_default == null)
- {
- lock (locker)
- {
- // 如果类的实例不存在则创建,否则直接返回
- if (_default == null)
- {
- _default = new EventBus();
- }
- }
- }
- return _default;
- }
- }
-
- ///
- ///
- ///
- ///
订阅的时候,就应该知道订阅什么事件 - ///
- public void Subscribe<T>( Action<object, MyEventArgs> eventHandler) where T:IEvent
- {
- lock (locker)
- {
- if (eventDic.ContainsKey(typeof(T)))
- {
- eventDic[typeof(T)].Add(eventHandler);
- }
- else
- {
- eventDic.Add(typeof(T), new List
object, MyEventArgs>>() { eventHandler }); - }
- }
- }
-
- public void Unsubscribe<T>(Action<object, MyEventArgs> eventHandler) where T : IEvent
- {
- lock (locker)
- {
- if (eventDic.ContainsKey(typeof(T)) && eventDic[typeof(T)].Contains(eventHandler))
- {
- eventDic[typeof(T)].Remove(eventHandler);
- }
- }
- }
-
- ///
- /// 发布
- ///
- /// 发布的时候,应该是发布具体的事件,而不是事件类型
- ///
- ///
- public virtual void Publish(IEvent e,object sender, MyEventArgs eventArgs)
- {
- lock (locker)
- {
- if (eventDic.ContainsKey(e.GetType()))
- {
- var subscriptions = eventDic[e.GetType()];
- for (int i = 0; i < subscriptions.Count; i++)
- {
- subscriptions[i](sender, eventArgs);
- }
- }
-
- }
- }
- }
eventDic就是上面说的维护的事件类型字典。记录了每个事件类型对应的要执行的所有Action。
EventBus全局唯一实例,这里使用Default实例。
有订阅,取消订阅,发布方法。也有用反射自动实现订阅的。
最后就是调用
- class Program
- {
- static void Main(string[] args)
- {
-
- SubscriberA suba = new SubscriberA();
- suba.Subscribe();
- PublisherA puba = new PublisherA();
- puba.Pub();
-
-
- SubscriberB subb = new SubscriberB();
- subb.Subscribe();
- //创建并发布事件
- EventBus.Default.Publish(new EventB() { EventName = "我是EventB" }
- , "Program"
- , new MyEventArgsA() { DateTime = DateTime.Now });
- }
- }
输出
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
比较好的讲事件总线的文章
过程中遇到一个问题:
类型M继承于N,但是Action
原因如下
例如
Action
Console.WriteLine(x.Length);
};
Action
b(new object())//无法执行,object无Length属性