c# 自己造轮子之——EventBus事件总线的使用-自己实现事件总线(一)

背景:之前公司由于开发人员少,相对业务并没有那么复杂,并没有涉及到事件总线EventBus,而到了新公司,看到类似发送邮件,发送短信的业务,调用非常方便,吸引了兴趣去一探究竟,

当然看起来很高深,但一旦搞懂原理后,它就没那么高深,这里写下来学习过程,一方面方便以后复习,另一个方面也希望能帮到别人

强烈建议大家亲自多写几遍,关于反射,泛型,接口,抽象类,抽象方法,虚方法,有一个深刻的认识

强烈建议大家亲自多写几遍,关于反射,泛型,接口,抽象类,抽象方法,虚方法,有一个深刻的认识

强烈建议大家亲自多写几遍,关于反射,泛型,接口,抽象类,抽象方法,虚方法,有一个深刻的认识

第一部分:为什么要有这个东西?

在C#中,我们可以在一个类中定义自己的事件,而其他的类可以订阅该事件,当某些事情发生时,可以通知到该类。

这对于桌面应用或者独立的windows服务来说是非常有用的。但对于一个web应用来说是有点问题的,因为对象都是在web请求中创建的,而且这些对象生命周期都很短,因而注册某些类的事件是很困难的。

此外,注册其他类的事件会使得类紧耦合。事件总线便可以用来解耦并重复利用应用中的逻辑。

好处比较明显,就是独立出一个发布订阅模块,调用者可以通过使用这个模块,屏蔽一些线程切换问题,简单地实现发布订阅功能。

坏处可能比较隐晦,但这些需要足够引起我们的重视

  • 大量的滥用,将导致逻辑的分散,出现问题后很难定位。
  • 没办法实现强类型,在编译的时候就发现问题。
  • 代码可读性有些问题,IDE无法识别这些协议,对IDE不友好。

总得来说,如果项目里面有大量的事件交互,那么还是可以通过EventBus来实现,否则还是推荐自己在模块内部实现观察者模式。

 

第二部分:原理及代码

原理很简单:

定义事件对象CZEventBase包括几个属性,

定义对事件的处理接口CZEventHandle,里面有对事件的处理

定义处理中心EventHandleService,提供一个集合,

这个集合是精髓,记录所有事件type对应的处理实现类

这个集合是精髓,记录所有事件type对应的处理实现类

这个集合是精髓,记录所有事件type对应的处理实现类

用来订阅(事件上将某事件对应处理类,添加到集合中),

发布事件(根据集合,以及事件type的key,从集合中找到调用当前事件对应的处理类,Invoke相关方法)

c# 自己造轮子之——EventBus事件总线的使用-自己实现事件总线(一)_第1张图片

项目下载地址:

链接:https://pan.baidu.com/s/1v9L9B2-nhl1KF1ycVJ-5ZA 
提取码:xve4 
复制这段内容后打开百度网盘手机App,操作更方便哦

备注:在项目中使用,自已派生对应事件和处理类,

使用这个模块很简单,就是CZEventBusFac.InitEventBus();

web一般直接放在global.aspx启动文件中,或是startup文件中初始化,(作用其实就是通过反射建立事件实现类,和对应处理类的对应关系集合,)
 调用就是           CZEventBusFac.Publish(new TestModuleEvent());

强烈建议大家亲自多写几遍,关于反射,泛型,接口,抽象类,抽象方法,虚方法,有一个深刻的认识

强烈建议大家亲自多写几遍,关于反射,泛型,接口,抽象类,抽象方法,虚方法,有一个深刻的认识

强烈建议大家亲自多写几遍,关于反射,泛型,接口,抽象类,抽象方法,虚方法,有一个深刻的认识

第三部分:代码难点,也是重点

通过反射获取某泛型接口下所有实现对象,涉及GetInterfaces()及GetGenericTypeDefinition()两个方法

根据泛型构造type对象itype,先获取所有type对象,where筛选出所有接口GetInterfaces()的type对象,
在where()的lambda表达式进一步利用t.GetGenericTypeDefinition()==itype选择接口构造type是itype

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CZEventBusCenter.CZEvent;
using CZEventBusCenter.CZEnum;
using CZEventBusCenter.CZEventHandle;
using System.Reflection;

namespace CZEventBusCenter.EventHandleService
{
    /// 
    /// 程序启动时,由反射的方式自动添加对应关系
    /// 
    public static class CZEventBusFac
    {
        /// 
        /// 定义一个字典集合,记录每一种事件对应的处理类,
        /// ***一个事件可能由多个类中的方法来处理***
        /// 
        public static Dictionary> _dic=new Dictionary>();
        /// 
        /// 获取所有继承特定接口下的实例对象,实现接口的泛型参数type类型作为字典集合key,
        /// 
        public static void InitEventBus() {
            //根据泛型构造type对象itype,先获取所有type对象,where筛选出所有接口GetInterfaces()的type对象,
            //在where()的lambda表达式进一步利用t.GetGenericTypeDefinition()==itype选择接口构造type是itype
            Type itype = typeof(ICZEventHandle<>);
            IEnumerable impleTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t=>!t.IsAbstract&&t.GetInterfaces().Any(it=>it.IsGenericType?it.GetGenericTypeDefinition()==itype:false));
            foreach (Type type in impleTypes)
            {
                //Type evtype = type.GetMethods().FirstOrDefault().GetParameters().FirstOrDefault().ParameterType;
                Type evtype = type.GetInterfaces().Where(it => it.IsGenericType ? it.GetGenericTypeDefinition() == itype : false).FirstOrDefault().GetGenericArguments().FirstOrDefault();
                ICZEventHandle eventHandle = (ICZEventHandle)Activator.CreateInstance(type);
                if (_dic.ContainsKey(evtype))
                {
                    _dic[evtype].Add(eventHandle);
                }
                else {
                    List list = new List();
                    list.Add(eventHandle);
                    _dic[evtype] = list;
                }
            }
        }
        /// 
        /// 发布事件即是找到对应处理类,执行相关方法操作
        /// 
        /// 
        public static void Publish(CZInterOpEvent ev) {
            //一般接口下没有指定相关方法,需要找到泛型接口下的方法
            foreach (ICZEventHandle eventHandle in _dic[ev.GetType()])
            {
                //eventHandle.GetType().GetMethods().FirstOrDefault().Invoke(eventHandle, new object[] { ev });
                //调用ICZEventHandle.EventHandle,实际上还是调用抽象方法LocalHandle()
                eventHandle.GetType().GetMethod(nameof(ICZEventHandle.EventHandle), BindingFlags.Instance | BindingFlags.Public).Invoke(eventHandle, new object[] { ev });
            }
        }

    }
}

泛型抽象类,虚方法,抽象方法 

强烈建议大家亲自多写几遍,关于反射,泛型,接口,抽象类,抽象方法,虚方法,有一个深刻的认识

强烈建议大家亲自多写几遍,关于反射,泛型,接口,抽象类,抽象方法,虚方法,有一个深刻的认识

强烈建议大家亲自多写几遍,关于反射,泛型,接口,抽象类,抽象方法,虚方法,有一个深刻的认识

 

你可能感兴趣的:(c#)