前言

话说EventBroker这玩意已经不是什么新鲜货了,记得第一次接触这玩意是在进第二家公司的时候,公司产品基础架构层中集成了分布式消息中间件,在.net基础服务层中使用EventBroker的模式将消息组装成事件,支持同域、跨域和跨机器进行事件的发布和订阅,后来才知道这玩意叫做EventBroker。不得不承认,这是一个非常聪明的东西,它在内部高度封装了消息和事件的处理,将上层应用的事件和委托的依赖进行解耦,并且提供非常简洁的方式进行开发。OK,本篇文章只是实现了一个简化版本的EventBroker,功能非常有限,也没有集成消息组件,所以也不支持跨域和跨机器的分布式应用了,甚至没有经过严格测试(比如并发处理、跨线程处理等等),社区上更好的实现很多。所以必须声明:只是实验性代码,纯粹练手,带着学习的态度写代码,coding不易,大家不喜勿喷

准备

本篇主要使用.net C#技术,包括委托、反射、弱引用和弱事件模式,写代码也是为了学习这些,找找码感。说明:为了不影响阅读,所贴代码均有一些阉割,如需源码,后续我把附件传上来,不过这源码真没啥东西,我上传就是自己做一个记录,存储下来

思路

思路不用多说,网上搜一下会有很多信息,我比较懒,直接贴一张网上的图可以明确表现出系统的交互场景和交互流程

应用

在应用层使用EventBroker,需要在应用中针对对象进行Register,一般中大型项目使用IOC容器都提供在对象创建时注入特征,比如:

// 示范代码
private void OnObjectBuild(ObjectModel builder, object instance)
{
var broker = DIContainer.Resolve<IEventBroker>();
broker.Register(instance);
}

这样比较方便,避免在程序中手动Register,不过我这里只是简单实现,不涉及IOC和任何设计的东西,注册对象后,在我们的类中可以像如下的方式实现:

[EventPublication("topic://eventbreaker/winform/message", EventScope = EventScope.Local)]
public event EventHandler<EventModelArgs<string>> MessageArrived;

以上是事件定义,我们可以把事件委托引用的方法定义在另一个类,而且两者完全不用依赖彼此

[EventSubscription("topic://eventbreaker/winform/message", EventScope = EventScope.Local)]
public void MessageReceived(object sender, EventModelArgs<string> message)
{
PrintOut(message.Data);
}

源码

  • 源码目录结构,看看是不是比别的版本精简了?

  • EventBroker,我暂且把这个叫事件总线,内部封装EventTopic,事件的发布者和订阅者信息都间接存储在总线上,除此之外,总线向外部提供统一的API接口,方便调用,相关接口如下:
namespace TinyEventBroker
{
public class EventBroker
{
private readonly EventTopicCollection topics
= new EventTopicCollection(); /// <summary>
/// 事件主题集合列表
/// </summary>
public EventTopicCollection Topics
{
get { return topics; }
} /// <summary>
/// 事件场景
/// </summary>
public virtual EventScope EventScope { get { return EventScope.Local; } } /// <summary>
/// 将对象注入到事件总线上
/// </summary>
public void Register(object target)
{
BindingFlags bingdings = BindingFlags.Instance | BindingFlags.Static
| BindingFlags.Public | BindingFlags.NonPublic;
RegisterPublication(target, bingdings);
RegisterSubscription(target, bingdings);
} /// <summary>
/// 注册发布者
/// </summary>
private void RegisterPublication(object target, BindingFlags bindings)
{
// ……
} /// <summary>
/// 注册订阅者
/// </summary>
/// <param name="target"></param>
/// <param name="bindings"></param>
private void RegisterSubscription(object target, BindingFlags bindings)
{
// ……
}
}
}
  • EventTopic,这个我把它称为事件主题,一个主题可以包含多个发布者和订阅者并提供一些处理,主题管理器内部将事件进行桥接,并动态调用订阅者方法,相关接口如下:
public class EventTopic
{
public EventTopic() { }
public EventTopic(string name)
: this()
{
this.Name = name;
} /// <summary>
/// 事件主题标识
/// </summary>
public string Name { get; set; } private List<EventPublication> publishers = new List<EventPublication>();
/// <summary>
/// 发布者列表
/// </summary>
internal List<EventPublication> Publishers
{
get { return publishers; }
set { publishers = value; }
} private List<EventSubscription> subscribers = new List<EventSubscription>();
/// <summary>
/// 订阅者列表
/// </summary>
internal List<EventSubscription> Subscribers
{
get { return subscribers; }
set { subscribers = value; }
} /// <summary>
/// 增加发布者
/// </summary>
internal void AddPublication(EventPublication publisher)
{
EventContext.Instance.WriteTo("主题:{0} 添加发布者{1}", Name, publisher);
publisher.EventFired += OnEventFired;
publishers.Add(publisher);
} /// <summary>
/// 增加订阅者
/// </summary>
internal void AddSubscription(EventSubscription subscriber)
{
EventContext.Instance.WriteTo("主题:{0} 添加订阅者{1}", Name, subscriber);
subscribers.Add(subscriber);
} /// <summary>
/// 移除发布者
/// </summary>
internal void RemovePublication(EventPublication publisher)
{
EventContext.Instance.WriteTo("主题:{0} 移除发布者{1}", Name, publisher);
publishers.Remove(publisher);
} /// <summary>
/// 移除订阅者
/// </summary>
internal void RemoveSubscription(EventSubscription subscriber)
{
EventContext.Instance.WriteTo("主题:{0} 移除订阅者{1}", Name, subscriber);
subscribers.Remove(subscriber);
} internal void OnEventFired(EventPublication publication, object sender, EventArgs args)
{
// ......
} private void CheckInvalidPublications()
{
// ......
} private void CheckInvalidSubscriptions()
{
// ......
}
}
  • Publisher,订阅者,使用弱引用来存储目标对象,防止目标对象无法释放造成内存泄露
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks; namespace TinyEventBroker
{
/// <summary>
/// 发布者
/// </summary>
internal class EventPublication
{
/// <summary>
/// 弱引用对象包装器
/// </summary>
private readonly WeakReference wrapper;
/// <summary>
/// 事件名称
/// </summary>
private readonly string eventName;
/// <summary>
/// 事件处理委托类型
/// </summary>
private readonly Type eventHandleType; /// <summary>
/// 对象名称,用于显示
/// </summary>
private readonly string targetName; /// <summary>
/// 事件定义,用来桥接对象的Event
/// </summary>
public event TopicEventHandler EventFired; public EventPublication(object target, string eventName)
{
wrapper = new WeakReference(target);
this.eventName = eventName;
this.targetName = target.GetType().Name; EventInfo info = target.GetType().GetEvent(EventName);
eventHandleType = info.EventHandlerType; Delegate handler = Delegate.CreateDelegate(
EventHandleType, this,
this.GetType().GetMethod("OnEventFired"));
info.AddEventHandler(target, handler);
} /// <summary>
/// 获取对象引用
/// </summary>
public object Target
{
get { return wrapper.Target; }
} /// <summary>
/// 对象名称,用于显示
/// </summary>
public string TargetName
{
get { return targetName; }
} /// <summary>
/// 判断对象是否存活
/// </summary>
public bool IsAlive
{
get { return wrapper.IsAlive; }
} /// <summary>
/// 事件名称
/// </summary>
public string EventName
{
get { return eventName; }
} /// <summary>
/// 事件处理类型
/// </summary>
public Type EventHandleType
{
get { return eventHandleType; }
} public virtual void OnEventFired(object sender, EventArgs e)
{
var handle = EventFired;
if (handle != null)
handle(this, sender, e);
} public override string ToString()
{
return string.Format("[{0} - {1}]", targetName, eventName);
}
}
}
  • 订阅者,依然使用弱引用来保存目标对象
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace TinyEventBroker
{
/// <summary>
/// 订阅者
/// </summary>
public class EventSubscription
{
/// <summary>
/// 弱引用对象包装器
/// </summary>
private readonly WeakReference wrapper;
/// <summary>
/// 方法名
/// </summary>
private readonly string methodName; /// <summary>
/// 对象名称,仅作显示
/// </summary>
private readonly string targetName; public EventSubscription(object target, string methodName)
{
this.wrapper = new WeakReference(target);
this.methodName = methodName;
targetName = target.GetType().Name;
} /// <summary>
/// 对象是否存活
/// </summary>
public bool IsAlive
{
get { return wrapper.IsAlive; }
} /// <summary>
/// 订阅者对象方法名
/// </summary>
public string MethodName { get { return methodName; } }
/// <summary>
/// 订阅者对象,通过弱引用包装访问
/// </summary>
public object Target { get { return wrapper.Target; } }
/// <summary>
/// 订阅者对象显式名
/// </summary>
public string TargetName { get { return targetName; } } internal virtual void HandleEvent(EventPublication publication, object sender, EventArgs args)
{
Delegate handler = Delegate.CreateDelegate(
publication.EventHandleType, Target, methodName); handler.DynamicInvoke(sender, args);
} public override string ToString()
{
return string.Format("[{0} - {1}]", targetName, methodName);
}
}
}
  • EventScope,这东西只是保留属性,以后扩展的时候用

初步测试结果

引用

针对弱引用这个东东,其实直接使用弱事件模式,微软已经封装好了的

Weak Event PatternsWeak Event in C#

结语

本篇只是EventBroker的简单实现,纯粹就是学习练手,我想,如果后续我有充足的时间,或者工作环境允许,我可以实现得更好,比如可以在内部封装多钟策略,异常处理策略、异步事件策略、处理并发、线程安全的考虑等等。更而甚之,可以将它进行抽象和扩展,加入消息中间件,实现分布式发布者和订阅者,那样就比较有实用价值了。不过写代码水平和效率有限,时间和精力也有限,工作是工作学习是学习,区区一文,聊表我的热情和开发,让代码和生活可以持续下去吧!

源码下载

最新文章

  1. -bash: ulimit: pipe size: cannot modify limit: Invalid argument
  2. centos 6.8 安装 nginx-1.11.4
  3. centos/redhat安装mysql
  4. Android虚拟机Classic qemu does not support SMP问题记录
  5. Eclipse常用开发插件
  6. RAC实例 表空间 维护
  7. Video事件、方法-- DOM
  8. 织梦CMS后台卡死的解决办法
  9. 有两个指针pa,pb分别指向有两个数,a,b,请写一个函数交换两个指针的指向,也就是让pa指向b,让pb指向a
  10. POJ3617 Best Cow Line
  11. 七、Solr服务部署和安全
  12. TensorFlow常用的函数
  13. 谈谈Python中的decorator装饰器,如何更优雅的重用代码
  14. Java 什么是线程安全
  15. css transition 实现滑入滑出
  16. pullMsg有感
  17. Tomcat热部署的三种方式
  18. wed
  19. gitlab Api接口使用
  20. asp.net Checkbox 绑定自定义属性

热门文章

  1. paip.自适应网页设计 同 响应 与设计的原理的差and实践总结
  2. SQLServer 存储过程嵌套事务处理
  3. 在ASP.NET 5应用程序中的跨域请求功能详解
  4. 脚本+批处理打造IIS监控器
  5. poj3417 Network 树形Dp+LCA
  6. Group By去除重复数据
  7. 【 D3.js 入门系列 --- 9.1 】 生产饼图
  8. Unity + iBatis + Asp.net Mvc 系统搭建
  9. crawler_爬虫开发的曲线图
  10. Cocos2d-x学习笔记(两)Cocos2d-x总体框架