我创建了一个简单的消息总线,用于排队和发布/发布事件。
我正在使用StructureMap来定位事件的已注册处理程序(Action<T>
),但我不确定如何将它从StructureMap返回的对象中转换为可调用的操作。
因为我无法转换为Action<object>
我假设Action<T>
不是协变的?这可以用另一种方式完成吗?
public class Bus
{
private ConcurrentQueue<object> events = new ConcurrentQueue<object>();
public void Queue<TEvent>(TEvent e)
{
events.Enqueue(e);
}
public void Emit()
{
object e;
while (events.TryDequeue(out e))
{
var handlerType = typeof(Action<>).MakeGenericType(e.GetType());
foreach (var handler in ObjectFactory.GetAllInstances(handlerType))
{
// how to invoke action?
}
}
}
}
答案 0 :(得分:5)
由于我无法转向Action,我假设Action不是协变的?
Action<T>
是逆变 - 这是有道理的,因为Action<object>
可以被视为Action<string>
(两者都可以接受string
参考)但是Action<string>
不能被视为Action<object>
(如果你提供了非字符串引用,你会期望它做什么?)。
调用它的最简单方法可能只是使用Delegate.DynamicInvoke
来说实话 - 另一种方法是编写泛型方法并使用反射或使用dynamic
调用它。
答案 1 :(得分:1)
如果在你的Queue
方法中排队不是事件,而是发出它们的代码,该怎么办?
private ConcurrentQueue<Action> handlers = new ConcurrentQueue<Action>();
public void Queue<TEvent>(TEvent e)
{
handlers.Enqueue(new Action(() =>
{
foreach (var handler in GetHandlers<TEvent>())
{
handler(e);
}
}));
}
public void Emit()
{
Action act;
while (handlers.TryDequeue(out act))
{
act();
}
}
private IEnumerable<Action<TEvent>> GetHandlers<TEvent>()
{
return ObjectFactory.GetAllInstances(typeof(Action<TEvent>>));
}
希望这有帮助。