如何将对象强制转换为Action <t> </t>

时间:2013-12-27 11:40:49

标签: c# generics

我创建了一个简单的消息总线,用于排队和发布/发布事件。

我正在使用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?
            }
        }
    }
}

2 个答案:

答案 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>>));
}

希望这有帮助。