什么是事件总线呢?官方的文档说,它是一个单例对象,由其他的类共同拥有,可以用来触发和处理事件。这个东西确实比较陌生,为什么要使用事件总线,或者说事件总线的优势是什么???首先我们可以明确的是,事件总线的出现是用来代替我们传统的事件的,那么我们传统的事件到底有什么不好呢??我们由浅入深,慢慢研究。

事件我们都比较熟悉,我们用两个例子回顾一下。

1.第一个例子,以前我们在winform的开发程序中到处都是事件,比如

sender:触发事件的对象

e:事件的数据,传递参数

方法中的内容:事件的处理逻辑。

2.第二个例子,发布-订阅模式

发布者:创建委托事件,然后调用委托事件(触发事件)

订阅者:编写事件的触发逻辑

以猫捉老鼠的故事为例子

 public class Cat
{
/// <summary>
/// 事件的处理逻辑
/// </summary>
/// <param name="mouseName"></param>
public void CatchMouse(string mouseName)
{
Console.WriteLine("小猫说:\n");
Console.WriteLine("我抓住了{0}",mouseName);
}
}

老鼠

 public  class Mouse
{
//定义委托
public delegate void CommingHandler(string name);
//声明事件委托
public event CommingHandler CommingEventHandler;
public Mouse(string name)
{
this.Name = name;
}
public void Comming(string name)
{
Console.WriteLine("老鼠说:\n"); Console.WriteLine("我的名字是{0},I am Comming!", name);
//发布事件
CommingEventHandler(name);
}
public string Name { get; set; } }

猫捉老鼠

 class Program
{
static void Main(string[] args)
{ Cat c1 = new Cat();
Mouse m1 = new Mouse("1号老鼠");
//订阅事件
m1.CommingEventHandler += c1.CatchMouse;
//触发事件
m1.Comming(m1.Name);
Console.ReadLine();
} }

从上面的两个例子中,我们可以总结一点东西,如何组成一个事件??

第一个例子:sender+e+事件的处理逻辑=事件

第二个例子:老鼠+老鼠的名字+CatchMouse的处理逻辑=事件

触发事件的对象+描述事件的参数+事件触发的处理逻辑=事件

前两个参数都是老鼠的参量统称为事件源,后一个参数是猫的动作统称为事件的处理,所以事件的本质就是事件源和事件的处理逻辑。

现在又有一个问题来了,如果出现一个需求,我们的猫要订阅一个猫吃一只多少斤鱼的事件FishCommingEventHandler,怎么做呢?此时事件源变成了鱼,事件的逻辑变成了”猫吃鱼“,这个动作,我们通常的做法一般都是重新定义一个委托事件,然后完成相应的处理逻辑。由此可以看出,我们的事件通用性很差劲,每次出现新的需求都需要重新写处理逻辑。变化的点:事件源和事件的处理。

二. 通用的发布-订阅模式

(1)抽象事件源

为了统一事件源的变化,我们定义一个抽象的父类

    /// <summary>
/// 抽象事件源接口
/// </summary>
public interface IEventData
{
/// <summary>
/// 触发事件的对象
/// </summary>
object EventSource { get; set; }
/// <summary>
/// 触发事件的时间
/// </summary>
DateTime EventTime { get; set; }
}
    /// <summary>
/// 所有的事件源都必须继承的父类,事件源就是参数,传递参数的作用,相当于老鼠的名字,鱼的名字
/// </summary>
public class EventData : IEventData
{
public object EventSource { get ; set ; }
public DateTime EventTime { get; set; }
public EventData()
{
EventTime = DateTime.Now;
}
}
/// <summary>
/// 自定义参数,封装变化
/// </summary>
public class SelfDefineEventData:EventData
{
public string MouseName { get; set; }
public float FishWeight{ get; set; }
}

(2)接下来就是对事件逻辑进行处理,统一事件触发的方法,如果事件的方法不同一,将会造成通用性变差,同时需要手动匹配事件源,所以需要注意的是事件源和事件的处理是绑定在一起的。

 /// <summary>
/// 标志接口,mark
/// </summary>
public interface IEventHandler
{ }
/// <summary>
/// 泛型接口,事件源和事件的处理是一起的
/// </summary>
/// <typeparam name="TEventData"></typeparam>
public interface IEventHandler<TEventData> : IEventHandler where TEventData : IEventData
{
/// <summary>
/// 根据事件源,做出相应的处理
/// </summary>
/// <param name="eventData"></param>
void Handle(TEventData eventData);
}

我们为事件1 (猫捉老鼠)和事件2 (猫吃了一只XX的鱼)定义一个统一的委托类

 public delegate void  SelfDefineEventHandler(SelfDefineEventData eventData);

修改我们老鼠的类和鱼类

  public  class Mouse
{ //声明事件委托
public event SelfDefineEventHandler selfEventHandler;
public Mouse(string name)
{
this.Name = name;
}
public void Comming(string name )
{
Console.WriteLine("老鼠说:\n"); Console.WriteLine("我的名字是{0},I am Comming!", name);
//触发事件
selfEventHandler(new SelfDefineEventData() { MouseName=name});
}
public string Name { get; set; } }
public class Fish
{
//声明事件委托
public event SelfDefineEventHandler selfEventHandler;
public Fish(int weight)
{
this.Weight = weight;
}
public void Comming(int weight)
{
Console.WriteLine("小鱼说:\n"); Console.WriteLine("我的重量是{0},I am Comming!", weight);
//触发事件
selfEventHandler(new SelfDefineEventData() { FishWeight = weight }); public int Weight { get; set; }
}

从上面的代码中,由于我们统一了事件源EventData,委托参数保证了一致性,所以实现了委托的共用。

修改我们的猫类

 public class Cat:IEventHandler<SelfDefineEventData>
{
/// <summary>
/// 事件的处理逻辑
/// </summary>
/// <param name="mouseName"></param>
public void CatchMouse(string mouseName)
{
Console.WriteLine("小猫说:\n");
Console.WriteLine("我抓住了{0}",mouseName);
} public void Handle(SelfDefineEventData eventData)
{
//处理猫捉老鼠
if(!string.IsNullOrEmpty(eventData.MouseName))
{
Console.WriteLine("小猫说:\n");
Console.WriteLine("我抓住了{0}", eventData.MouseName); }
//处理猫吃鱼
if (eventData.FishWeight != )
{
Console.WriteLine("小猫说:\n");
Console.WriteLine("我吃了{0}斤鱼", eventData.FishWeight);
}
}
}

猫捉老鼠和猫吃鱼

  static void Main(string[] args)
{ Cat c1 = new Cat();
Mouse m1 = new Mouse("1号老鼠");
Fish f1 = new Fish();
//订阅事件
m1.selfEventHandler += c1.Handle;
//这边的事件变量定义的名字一样了,可以用不同的名字区分,这是事件不是委托
f1.selfEventHandler += c1.Handle;
//触发事件
m1.Comming(m1.Name);
f1.Comming(f1.Weight);
Console.ReadLine();
}

从上面的代码可以看出我们统一了事件处理逻辑为Handle

上面的代码从逻辑上来看,每次都是同时触发执行了所有的注册事件,那么如果我需要只触发一个事件????那么我们只需要对传入参数EventData进行判断即可,方案有两个:

1.直接在SelfDefineEventData中将EventSource赋值为Mouse和Fish,然后在Handle方法中判断EventSource的类型

这种方式是承接上面的实现代码的,突然发现一个问题,上面的代码虽然可以跑的通,但是有一种过度封装的感觉,本来每一个XXEventData都是继承自EventData,都是一个独立的模块,所以我们上面的代码违背了单一性原则

2.分别定义两个EventData:FishEventData和MouseEventData,然后在Handle中判断eventData的类型,所以我们采用这种方法,并将前面的代码重新修改

定义事件源

    public class MouseEventData : EventData
{
public string MouseName { get; set; }
}
public class FishEventData : EventData
{
public float FishWeight { get; set; }
}

修改鱼和老鼠类

  public  class Mouse
{ //声明事件委托
public event MouserEventHandler selfEventHandler;
public Mouse(string name)
{
this.Name = name;
}
public void Comming(string name )
{
Console.WriteLine("老鼠说:\n"); Console.WriteLine("我的名字是{0},I am Comming!", name);
//触发事件
selfEventHandler(new MouseEventData() { MouseName = name});
}
public string Name { get; set; } }
public class Fish
{
//声明事件委托
public event FishEventHandler selfEventHandler;
public Fish(float weight)
{
this.Weight = weight;
}
public void Comming(float weight)
{
Console.WriteLine("小鱼说:\n"); Console.WriteLine("我的重量是{0},I am Comming!", weight);
//触发事件
selfEventHandler(new FishEventData() { FishWeight = weight });
}
public float Weight { get; set; }
}

定义不同的事件委托

    public delegate void MouserEventHandler(MouseEventData eventData );
public delegate void FishEventHandler(FishEventData eventData);

修改猫类

 public class Cat:IEventHandler<IEventData>
{ public void Handle(IEventData eventData)
{
if (eventData is MouseEventData)
{
var mouseData = (MouseEventData)eventData;
//处理猫捉老鼠
if (!string.IsNullOrEmpty(mouseData.MouseName))
{
Console.WriteLine("小猫说:\n");
Console.WriteLine("我抓住了{0}", mouseData.MouseName); }
}
if (eventData is FishEventData)
{
var fishData = (FishEventData)eventData;
//处理猫吃鱼
if (fishData.FishWeight != )
{
Console.WriteLine("小猫说:\n");
Console.WriteLine("我吃了{0}斤鱼", fishData.FishWeight);
}
}
}
}

重新实现猫捉老鼠

  class Program
{
static void Main(string[] args)
{ Cat c1 = new Cat();
Mouse m1 = new Mouse("1号老鼠"); //订阅事件
m1.selfEventHandler += c1.Handle;
//触发事件
m1.Comming(m1.Name); Console.ReadLine();
} }

发现一个问题

原来父类参数事件可以赋值给子类参数事件上,哈哈

到此为止,我们的发布订阅模式已经完成,所以每次有新的需求,只需要分别定制XXEventData,体现了一种模块化,单一性的思想,同时将事件处理进行统一,体现了封装统一的思想,两者结合恰到好处。

参考文章:事件知多少https://www.jianshu.com/p/22fbe7a7c120

ABP之事件总线(1)的更多相关文章

  1. ABP之事件总线(5)

    前面已经对Castle Windsor的基本使用进行了学习,有了这个基础,接下来我们将把我们的事件总线再次向ABP中定义的事件总线靠近.从源码中可以知道在ABP中定义了Dictionary,存放三种类 ...

  2. ABP之事件总线(4)

    在上一篇的随笔中,我们已经初步完成了EventBus,但是EventBus中还有诸多的问题存在,那么到底有什么问题呢,接下来我们需要看一看ABP中的源码是如何定义EventBus的. 1.第一个点 在 ...

  3. ABP之事件总线(3)

    承接上一篇时间总线的学习,在上一篇中我们实现了取消显式注册事件的方式,采用使用反射的方式.这样的好处可以解除Publisher和Scriber的显式依赖,但是问题又来了,因为我们只有Publisher ...

  4. ABP的事件总线和领域事件(EventBus & Domain Events)

    http://www.aspnetboilerplate.com/Pages/Documents/EventBus-Domain-Events EventBus EventBus是个单例,获得Even ...

  5. ABP之事件总线(2)

    在上一篇文章中,我们复习了一下事件的经典的发布订阅模式,同时对是事件源和时间处理逻辑进行抽象统一,用起来也没有问题.但是还是有很多的问题,比如说我们Handle方法其实是违背了单一性的原则的,里面混杂 ...

  6. [Abp 源码分析]九、事件总线

    0.简介 事件总线就是订阅/发布模式的一种实现,本质上事件总线的存在是为了降低耦合而存在的. 从上图可以看到事件由发布者发布到事件总线处理器当中,然后经由事件总线处理器调用订阅者的处理方法,而发布者和 ...

  7. ABP理论学习之事件总线和领域事件

    返回总目录 本篇目录 事件总线 定义事件 触发事件 处理事件 句柄注册 取消注册 在C#中,我们可以在一个类中定义自己的事件,而其他的类可以注册该事件,当某些事情发生时,可以通知到该类.这对于桌面应用 ...

  8. ABP官方文档翻译 3.7 领域事件(事件总线)

    领域事件(事件总线) 事件总线 注入IEventBus 获取默认实例 定义事件 预定义事件 处理异常 实体更改 触发事件 处理事件 处理基础事件 处理者异常 处理多个事件 注册处理者 自动 手动 取消 ...

  9. ABP EventBus(事件总线)

    事件总线就是订阅/发布模式的一种实现    事件总线就是为了降低耦合 1.比如在winform中  到处都是事件 触发事件的对象  sender 事件的数据    e 事件的处理逻辑  方法体 通过E ...

随机推荐

  1. 浅谈压缩感知(二十三):压缩感知重构算法之压缩采样匹配追踪(CoSaMP)

    主要内容: CoSaMP的算法流程 CoSaMP的MATLAB实现 一维信号的实验与结果 测量数M与重构成功概率关系的实验与结果 一.CoSaMP的算法流程 压缩采样匹配追踪(CompressiveS ...

  2. Java 之外,是 Scala 还是 Groovy?【转载】

    原文地址 Scala 和 Groovy 都是基于 JVM 的语言,相比 Java,它们都有语法更加简明和表达能力更丰富.对于那些既想不脱离开 JVM 又想避免 Java 繁琐语句的开发人员来说,Sca ...

  3. 原创:vsphere概念深入系列三:vSphere命令行管理

    假设无法近距离接触物理主机,只能远程命令行管理,. 以下命令行可以起到点作用. 首先需要安装vSphere CLI工具. 启动后界面: 1.查看datastore内容 所有命令行工具都可以加上-ser ...

  4. grep 多行 正则匹配

    https://stackoverflow.com/questions/2686147/how-to-find-patterns-across-multiple-lines-using-grep I ...

  5. Jenkins常用插件

    Generic Webhook Trigger Plugin触发器webhook用户触发构建 Deploy to container Plugin部署到tomcat Gradle Plugin Gra ...

  6. Microsoft/Git-Credential-Manager-for-Mac-and-Linux

    纠正Mac上的错误: Fatal: java.lang.Error encountered. Details: unexpected errorfatal: credential helper '!/ ...

  7. linux一些稍微高级的命令

    cut 命令:提取文件中指定的字段(列).字符内容.单词的默认分隔符为空格. cut [选项]... [文件]... 选项: -b  按照字节切 -c  按照字符切 -d  指定分割符 -f   指定 ...

  8. shiny: Web Application Framework for R

    shiny: Web Application Framework for R 基于R语言的一个web框架,适用于数据分析与图表绘画展示类型的网站.

  9. iOS的动态代理模式的实现

    动态代理模式的应用很多,特别是在不能修改被代理类的前提下,要对执行某些方法时需要打log或者捕捉异常等处理时,是一个非常方便的方法.只需要少量修改客户端(场景类)代码和添加一个代理类就可以实现,这个符 ...

  10. 菜鸟教程之工具使用(六)——让Maven项目直接在eclipse内部的Tomcat中运行

    Hello,大家好,好久不见!最近终于安定下来了,可以静下心来写东西了.先写篇简单的,找找感觉.工具系列的本身就比较简单,没什么技术含量.因为说到底,工具只是辅助我们工作的,知道怎么用,然后剩下的就是 ...