如何将对象转换为 Action<T>

3

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

我正在使用 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个回答

6

由于我无法将类型转换为Action,我假设Action不是协变的?

Action<T>逆变的 - 这是有道理的,因为一个 Action<object> 可以被视为一个 Action<string>(两者都可以接受一个字符串引用),但是一个 Action<string> 不能被视为一个 Action<object>(如果您提供了一个非字符串引用,您期望它会做什么?)。

调用这个最简单的方法可能是直接使用 Delegate.DynamicInvoke - 另一种选择是编写一个通用方法并使用反射或使用 dynamic 调用它。


我能否介绍一下我的逆变接口,用于处理程序,允许将泛型类型向下转换?我尝试使用 IHandle<in T>,但它不允许我向下转换为 IHandle<object> - Ben Foster
@BenFoster:不,正是因为它是逆变的,而你试图将其作为协变使用。你尝试过我建议的任何一种方法吗? - Jon Skeet
我尝试过使用DynamicInvoke,它可以工作,但我读到它比Invoke的开销更大。我想另一个选择是将事件的处理排队,这样我就可以避免任何类型转换问题,例如queue.Enqueue(() => Handle<TEvent>(e)) - Ben Foster
@BenFoster:是的,DynamicInvoke会有相当大的开销 - 但是性能对你来说有多大问题?不过,你接受的答案似乎很好。 - Jon Skeet
我不确定(没有基准测试)但事件将被同步处理,因此性能是一个因素。我认为我还是会坚持接受的答案,因为它是一个相当好的解决方案。 - Ben Foster

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

希望这有所帮助。

问题在于处理事件时我没有类型参数。如果你再看一下我的代码示例,你会发现它们被排队,然后稍后处理。 - Ben Foster
抱歉,我没注意到。我已经重新写了我的答案。 - volpav
我刚刚得出了同样的结论。这肯定解决了任何类型转换问题。 - Ben Foster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接