事件总线EventBus

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

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

事件总线EventBus_第1张图片

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


实现方式:

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

代码实现:

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

        /// 
		/// 事件源
		/// 所有被订阅的事件都要继承这个
		/// 
		public abstract class IEvent
		{
				/// 
				/// 
				/// 
				public string EventName;
		}

		public abstract class IEvent: IEvent where T : MyEventArgs
		{
		}

        public class EventA :IEvent
		{
		}

        public class EventB : IEvent
		{
		}

事件参数

        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 where T:IEvent
		{
				/// 
				/// 发布事件
				/// 
				void Pub();
		}

        /// 
		/// 发布者
		/// 
		/// 
		public class PublisherA : IPublisher
		{
				/// 
				/// 发布事件
				/// 
				public void Pub()
				{
						//创建事件并发布
						EventBus.Default.Publish(new EventA() { EventName = "我是EventA" }
						, this
						, new MyEventArgsA() { DateTime = DateTime.Now });
				}
		}

 订阅者:

        /// 
		/// 观察者(订阅者)接口
		/// 规范了订阅的事件类型
		/// 
		public abstract class ISubscriber
		{
				public abstract void Subscribe(Action eventHandler);

		}

        public class SubscriberA : ISubscriber
		{
				/// 
				/// 订阅者应该知道自己要执行什么。当然也可以从外部修改
				/// 
				public Action 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 eventHandler)
				{
						EventBus.Default.Subscribe(eventHandler);
				}
		}

        public class SubscriberB : ISubscriber
		{
				/// 
				/// 订阅者应该知道自己要执行什么。所以这里直接定义了一个Action.
				/// 当然也可以从外部传入Action
				/// 
				public Action 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 eventHandler) 
				{
						EventBus.Default.Subscribe(eventHandler);
				}
				 
		}

终于到事件总线了。

        public class EventBus
		{
        private static EventBus _default;
        protected static readonly object locker = new object();
        private Dictionary>> eventDic = new Dictionary>>();
        public static EventBus Default
        {
            get
            {
                if (_default == null)
                {
                    lock (locker)
                    {
                        // 如果类的实例不存在则创建,否则直接返回
                        if (_default == null)
                        {
                            _default = new EventBus();
                        }
                    }
                }
                return _default;
            }
        }

        /// 
        /// 
        /// 
        /// 订阅的时候,就应该知道订阅什么事件
        /// 
        public void Subscribe( Action eventHandler) where T:IEvent
        {
            lock (locker)
            {
                if (eventDic.ContainsKey(typeof(T)))
                {
                    eventDic[typeof(T)].Add(eventHandler);
								}
								else
								{
                    eventDic.Add(typeof(T), new List>() { eventHandler });
                }
            }
        }

        public void Unsubscribe(Action 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

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

什么是事件总线 - 走看看

 详解c# 事件总线

过程中遇到一个问题:

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

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

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

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

你可能感兴趣的:(C#,事件总线,发布,订阅)