C# 如何将目标付诸行动<;T>;

C# 如何将目标付诸行动<;T>;,c#,generics,C#,Generics,我已经创建了一个简单的消息总线,用于排队和发送/发布事件 我正在使用StructureMap查找事件的已注册处理程序(Action),但不确定如何将它从StructureMap返回的对象强制转换为可调用的操作 因为我不能强制转换到动作我假设动作不是协变的?这可以用另一种方法吗 public class Bus { private ConcurrentQueue<object> events = new ConcurrentQueue<object>();

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

我正在使用StructureMap查找事件的已注册处理程序(
Action
),但不确定如何将它从StructureMap返回的对象强制转换为可调用的操作

因为我不能强制转换到
动作
我假设
动作
不是协变的?这可以用另一种方法吗

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?
            }
        }
    }
}
公共类总线
{
私有ConcurrentQueue事件=新ConcurrentQueue();
公共无效队列(TEvent e)
{
事件。排队(e);
}
公共空间排放()
{
对象e;
while(events.TryDequeue(out e))
{
var handlerType=typeof(Action).MakeGenericType(e.GetType());
foreach(ObjectFactory.GetAllInstances(handlerType)中的变量处理程序)
{
//如何调用操作?
}
}
}
}
既然我不能付诸行动,我就假设行动不是协变的

动作
是反变的-这很有意义,因为
动作
可以被视为
动作
(两者都可以接受
字符串
引用),但是
动作
不能被视为
动作
(如果提供非字符串引用,您希望它做什么?)


调用此函数的最简单方法可能是诚实地使用—另一种方法是编写一个通用方法,并通过反射或使用
动态
调用它,如果在
队列
方法中,您排队的不是事件,而是发出事件的代码,该怎么办

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>>));
}
private ConcurrentQueue handlers=new ConcurrentQueue();
公共无效队列(TEvent e)
{
handlers.Enqueue(新操作(()=>
{
foreach(GetHandlers()中的变量处理程序)
{
处理程序(e);
}    
}));
}
公共空间排放()
{
行动法;
while(handlers.TryDequeue(out-act))
{
act();
}
}
私有IEnumerable GetHandlers()
{
返回ObjectFactory.GetAllInstances(typeof(Action>);
}

希望这能有所帮助。

问题是在处理事件时我没有类型参数。如果您再次查看我的代码示例,您可以看到它们已排队,然后稍后再处理。对不起,没有发现这一点。我已经写了我的答案。我刚刚得出了同样的结论。这当然可以解决任何类型转换问题。我是否可以为允许对泛型类型进行向下转换的处理程序引入我的逆变接口?我试过
IHandle
,但它不会让我失望于
IHandle
@BenFoster:不,正是因为它是反变的,你正试图协变地使用它。您尝试过我建议的任何一种方法吗?我尝试过
DynamicInvoke
这一方法,但我了解到这比
Invoke
的开销更大。我想另一种选择是对事件的处理进行排队,这样我可以避免任何铸造问题,例如
queue.Enqueue(()=>Handle(e))
@BenFoster:是的,
DynamicInvoke
将有相当大的开销-但是性能对您来说是一个多大的问题?您接受的答案似乎不错。我不确定(没有基准测试),但事件将同步处理,因此性能是一个因素。我想我会坚持公认的答案,因为这是一个很好的解决方案。