我希望创建一个非常简单的事件总线,它允许任何客户端订阅特定类型的事件,并且当任何发布者使用
EventBus.PushEvent() 方法将事件推送到总线上时,只有订阅了该特定事件类型的客户端才会收到该事件。
我正在使用 C# 和 .NET 2.0。
Tiny Messenger是一个不错的选择,我已经在一个实时项目中使用了2.5年。以下是一些来自Wiki的代码示例(链接如下):
发布
messageHub.Publish(new MyMessage());
订阅
messageHub.Subscribe<MyMessage>((m) => { MessageBox.Show("Message Received!"); });
messageHub.Subscribe<MyMessageAgain>((m) => { MessageBox.Show("Message Received!"); }, (m) => m.Content == "Testing");
这份代码在 GitHub 上: https://github.com/grumpydev/TinyMessenger
维基页面在这里: https://github.com/grumpydev/TinyMessenger/wiki
此外还有一个 Nuget 包。
Install-Package TinyMessenger
这是受安卓EventBus启发的一个库,但更为简单:
public class EventBus
{
public static EventBus Instance { get { return instance ?? (instance = new EventBus()); } }
public void Register(object listener)
{
if (!listeners.Any(l => l.Listener == listener))
listeners.Add(new EventListenerWrapper(listener));
}
public void Unregister(object listener)
{
listeners.RemoveAll(l => l.Listener == listener);
}
public void PostEvent(object e)
{
listeners.Where(l => l.EventType == e.GetType()).ToList().ForEach(l => l.PostEvent(e));
}
private static EventBus instance;
private EventBus() { }
private List<EventListenerWrapper> listeners = new List<EventListenerWrapper>();
private class EventListenerWrapper
{
public object Listener { get; private set; }
public Type EventType { get; private set; }
private MethodBase method;
public EventListenerWrapper(object listener)
{
Listener = listener;
Type type = listener.GetType();
method = type.GetMethod("OnEvent");
if (method == null)
throw new ArgumentException("Class " + type.Name + " does not containt method OnEvent");
ParameterInfo[] parameters = method.GetParameters();
if (parameters.Length != 1)
throw new ArgumentException("Method OnEvent of class " + type.Name + " have invalid number of parameters (should be one)");
EventType = parameters[0].ParameterType;
}
public void PostEvent(object e)
{
method.Invoke(Listener, new[] { e });
}
}
}
应用场景:
public class OnProgressChangedEvent
{
public int Progress { get; private set; }
public OnProgressChangedEvent(int progress)
{
Progress = progress;
}
}
public class SomeForm : Form
{
// ...
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
EventBus.Instance.Register(this);
}
public void OnEvent(OnProgressChangedEvent e)
{
progressBar.Value = e.Progress;
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
EventBus.Instance.Unregister(this);
}
}
public class SomeWorkerSomewhere
{
void OnDoWork()
{
// ...
EventBus.Instance.PostEvent(new OnProgressChangedEvent(progress));
// ...
}
}
[Publishes("TimerTick")]
public event EventHandler Expired;
private void OnTick(Object sender, EventArgs e)
{
timer.Stop();
OnExpired(this);
}
[SubscribesTo("TimerTick")]
public void OnTimerExpired(Object sender, EventArgs e)
{
EventHandler handlers = ChangeLight;
if(handlers != null)
{
handlers(this, EventArgs.Empty);
}
currentLight = ( currentLight + 1 ) % 3;
timer.Duration = lightTimes[currentLight];
timer.Start();
}
另一个好的实现可以在以下找到:
http://code.google.com/p/fracture/source/browse/trunk/Squared/Util/EventBus.cs
使用案例可在以下位置访问: /trunk/Squared/Util/UtilTests/Tests/EventTests.cs
此实现不需要外部库。
一个改进可能是能够使用类型而不是字符串进行订阅。
https://github.com/RemiBou/RemiDDD/tree/master/RemiDDD.Framework/Cqrs
有一个依赖项需要使用Ninject。 如果您想观察事件,请实现"IObserver",如果您想处理命令,请实现"ICommandHandleer"。
你应该看一下Hibernating Rhinos的第三集,Ayende的屏幕录制系列 - "实现事件代理"。
它展示了如何使用Windsor实现一个非常简单的事件代理来连接事物。源代码也包括在内。
所提出的事件代理解决方案非常简单,但是增加解决方案以允许在事件中传递参数不需要太多时间。