0%

C#总结(六)EventBus事件总线的使用-自己实现事件总线 - 章为忠 - 博客园

Excerpt

在C#中,我们可以在一个类中定义自己的事件,而其他的类可以订阅该事件,当某些事情发生时,可以通知到该类。这对于桌面应用或者独立的windows服务来说是非常有用的。但对于一个web应用来说是有点问题的,因为对象都是在web请求中创建的,而且这些对象生命周期都很短,因而注册某些类的事件是很困难的。此外


在C#中,我们可以在一个类中定义自己的事件,而其他的类可以订阅该事件,当某些事情发生时,可以通知到该类。这对于桌面应用或者独立的windows服务来说是非常有用的。但对于一个web应用来说是有点问题的,因为对象都是在web请求中创建的,而且这些对象生命周期都很短,因而注册某些类的事件是很困难的。此外,注册其他类的事件会使得类紧耦合。事件总线便可以用来解耦并重复利用应用中的逻辑。

事件总线带来的好处和引入的问题

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

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

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

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

示例代码

所以今天介绍一个简单的事件总线,它是事件发布订阅模式的实现,让我们能在领域驱动设计(DDD)中以事件的弱引用本质对我们的模块和领域边界很好的解耦设计。

目前,所有的源代码已经提交到github 上,地址:https://github.com/weizhong1988/Weiz.EventBus

 程序目录结构如下:

事件总线

事件总线是被所有触发并处理事件的其他类共享的单例对象。要使用事件总线,首先应该获得它的一个引用。下面有两种方法来处理:

订阅事件

触发事件之前,应该先要定义该事件。EventBus为我们提供了Subscribe 方法来订阅事件:

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<span>public</span> <span>void</span> Subscribe&lt;TEvent&gt;(IEventHandler&lt;TEvent&gt; eventHandler) <span>where</span><span> TEvent : IEvent
{
</span><span>//</span><span>同步锁</span>
<span>lock</span><span> (_syncObject)
{
</span><span>//</span><span>获取领域模型的类型</span>
<span>var</span> eventType = <span>typeof</span><span>(TEvent);
</span><span>//</span><span>如果此领域类型在事件总线中已注册过</span>
<span>if</span><span> (_dicEventHandler.ContainsKey(eventType))
{
</span><span>var</span> handlers =<span> _dicEventHandler[eventType];
</span><span>if</span> (handlers != <span>null</span><span>)
{
handlers.Add(eventHandler);
}
</span><span>else</span><span>
{
handlers </span>= <span>new</span> List&lt;<span>object</span>&gt;<span>
{
eventHandler
};
}
}
</span><span>else</span><span>
{
_dicEventHandler.Add(eventType, </span><span>new</span> List&lt;<span>object</span>&gt;<span> { eventHandler });
}
}
}</span>

复制代码

所以的事件都集成自IEvent,该类包含了类处理事件需要的属性。

复制代码

1
2
3
4
5
6
7
<span>var</span> sendEmailHandler = <span>new</span><span> UserAddedEventHandlerSendEmail();
</span><span>var</span> sendMessageHandler = <span>new</span><span> UserAddedEventHandlerSendMessage();
</span><span>var</span> sendRedbagsHandler = <span>new</span><span> UserAddedEventHandlerSendRedbags();
Weiz.EventBus.Core.EventBus.Instance.Subscribe(sendEmailHandler);
Weiz.EventBus.Core.EventBus.Instance.Subscribe(sendMessageHandler);
//</span><span>Weiz.EventBus.Core.EventBus.Instance.Subscribe&lt;UserGeneratorEvent&gt;(sendRedbagsHandler);</span>
Weiz.EventBus.Core.EventBus.Instance.Subscribe&lt;OrderGeneratorEvent&gt;(sendRedbagsHandler);

复制代码

发布事件

对于事件源,则可以通过Publish 方法发布事件。触发一个事件很简单,如下所示:

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<span>     public</span> <span>void</span> Publish&lt;TEvent&gt;(TEvent tEvent, Action&lt;TEvent, <span>bool</span>, Exception&gt; callback) <span>where</span><span> TEvent : IEvent
{
</span><span>var</span> eventType = <span>typeof</span><span>(TEvent);
</span><span>if</span> (_dicEventHandler.ContainsKey(eventType) &amp;&amp; _dicEventHandler[eventType] != <span>null</span> &amp;&amp;<span>
_dicEventHandler[eventType].Count </span>&gt; <span>0</span><span>)
{
</span><span>var</span> handlers =<span> _dicEventHandler[eventType];
</span><span>try</span><span>
{
</span><span>foreach</span> (<span>var</span> handler <span>in</span><span> handlers)
{
</span><span>var</span> eventHandler = handler <span>as</span> IEventHandler&lt;TEvent&gt;<span>;
eventHandler.Handle(tEvent);
callback(tEvent, </span><span>true</span>, <span>null</span><span>);
}
}
</span><span>catch</span><span> (Exception ex)
{
callback(tEvent, </span><span>false</span><span>, ex);
}
}
</span><span>else</span><span>
{
callback(tEvent, </span><span>false</span>, <span>null</span><span>);
}
}</span>

复制代码

下面是发布事件的调用:

1
2
3
4
5
<span>var</span> orderGeneratorEvent = <span>new</span> OrderGeneratorEvent { OrderId =<span> Guid.NewGuid() };

System.Console.WriteLine(</span><span>"</span><span>{0}下单成功</span><span>"</span><span>, orderGeneratorEvent.OrderId);

Weiz.EventBus.Core.EventBus.Instance.Publish(orderGeneratorEvent, CallBack);</span>

定义处理事件

要处理一个事件,应该要实现IEventHandler接口,如下所示:

复制代码

1
2
3
4
5
6
7
8
9
10
11
<span>///</span> <span>&lt;summary&gt;</span>
<span>///</span><span> send email
</span><span>///</span> <span>&lt;/summary&gt;</span>
<span>public</span> <span>class</span> UserAddedEventHandlerSendEmail : IEventHandler&lt;UserGeneratorEvent&gt;<span>
{

</span><span>public</span> <span>void</span><span> Handle(UserGeneratorEvent tEvent)
{
System.Console.WriteLine(</span><span>string</span>.Format(<span>"</span><span>{0}的邮件已发送</span><span>"</span><span>, tEvent.UserId));
}
}</span>

复制代码

处理多事件

在一个单一的处理句柄中,可以处理多个事件。这时,你应该为每个事件实现IEventHandler。比如:

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<span>///</span> <span>&lt;summary&gt;</span>
<span>///</span><span> red bags.
</span><span>///</span> <span>&lt;/summary&gt;</span>
<span>public</span> <span>class</span> UserAddedEventHandlerSendRedbags : IEventHandler&lt;UserGeneratorEvent&gt;,IEventHandler&lt;OrderGeneratorEvent&gt;<span>
{
</span><span>public</span> <span>void</span><span> Handle(OrderGeneratorEvent tEvent)
{
System.Console.WriteLine(</span><span>string</span>.Format(<span>"</span><span>{0}的下单红包已发送</span><span>"</span><span>, tEvent.OrderId));
}

</span><span>public</span> <span>void</span><span> Handle(UserGeneratorEvent tEvent)
{
System.Console.WriteLine(</span><span>string</span>.Format(<span>"</span><span>{0}的注册红包已发送</span><span>"</span><span>, tEvent.UserId));
}
}</span>

复制代码

最后

以上,就把事件总线介绍完了,完整的代码,请到github 上下载,这个只是EventBus 的简单实现,各位可以根据自己的实际场景和需求,优化修改。