使用MulticastDelegate作为参数,同时避免使用DynamicInvoke

时间:2011-01-20 19:46:06

标签: c# events delegates multicastdelegate

我有MulticastDelegate可以引用具有相同签名的许多(遗留)代理中的一个。例如:

public delegate void ObjectCreated(object sender, EventArgs args);
public delegate void ObjectDeleted(object sender, EventArgs args);
//...

然后,这些代表用于定义事件:

public event ObjectCreated ObjectWasCreated;
public event ObjectDeleted ObjectWasDeleted;

然后我有一个方法,它接受MulticastDelegate我用来做一些常见的检查:

void DispatchEvent(MulticastDelegate handler, object sender, EventArgs args)
{
    if (handler != null)
    {
        // ...
        handler.DynamicInvoke(sender, args);
    }
}

从定义事件的类的其他方法中调用:

DispatchEvent(ObjectWasCreated, sender, args);
DispatchEvent(ObjectWasDeleted, sender, args);

有没有更简洁的方法来避免DynamicInvoke?

3 个答案:

答案 0 :(得分:2)

这是我的无反射解决方案。它基本上将多播委托实现为列表。更少的代码?不是。性能更好?我不知道。清洁器? MEH。

public delegate void ObjectCreated(object sender, EventArgs args);
public delegate void ObjectDeleted(object sender, EventArgs args);

public event ObjectCreated ObjectWasCreated
{
    add
    {
        m_ObjectCreatedSubscribers.Add(value.Invoke);
    }
    remove
    {
        m_ObjectCreatedSubscribers.RemoveAll(e => e.Target.Equals(value));
    }
}
public event ObjectDeleted ObjectWasDeleted
{
    add
    {
        m_ObjectDeletedSubscribers.Add(value.Invoke);
    }
    remove
    {
        m_ObjectDeletedSubscribers.RemoveAll(e => e.Target.Equals(value));
    }
}

private List<Action<object, EventArgs>> m_ObjectCreatedSubscribers = new List<Action<object, EventArgs>>();
private List<Action<object, EventArgs>> m_ObjectDeletedSubscribers = new List<Action<object, EventArgs>>();

void DispatchEvent(List<Action<object, EventArgs>> subscribers, object sender, EventArgs args)
{
    foreach (var subscriber in subscribers)
        subscriber(sender, args);
}

答案 1 :(得分:1)

一个简单的替代方法是使用内置类型,例如Action<,>EventHandler,而不是自定义委托,以便获得强大的类型。

public static event Action<object, EventArgs> ObjectWasCreated;
public static event Action<object, EventArgs> ObjectWasDeleted;  

void DispatchEvent(Action<object, EventArgs> handler, object sender, EventArgs args) 
{
    if (handler != null)
    {
        // ...
        handler(sender, args);
    }
}

public static event EventHandler ObjectWasCreated;
public static event EventHandler ObjectWasDeleted;  

void DispatchEvent(EventHandler handler, object sender, EventArgs args) 
{
    if (handler != null)
    {
        // ...
        handler(sender, args);
    }
}

现在你的方法调用很简单。

DispatchEvent(ObjectWasCreated, sender, args);
DispatchEvent(ObjectWasDeleted, sender, args);

但这绝不是一个好的解决方案。

您可以使用dynamic,仍然比DynamicInvoke更好:

void DispatchEvent(MulticastDelegate handler, object sender, EventArgs args) 
{
    if (handler != null)
    {
        // ...
        ((dynamic)handler)(sender, args);
    }
}

或者可能是泛型:

void DispatchEvent<T>(T handler, object sender, EventArgs args) 
{
    if (handler != null)
    {
        // ...
        ((dynamic)handler)(sender, args);
    }
}

我做了一个小的效果比较,发现dynamic实际上太好了

  

对于百万次尝试

     

MulticastDelegate + dynamic(第一个例子)=&gt; 40毫秒

     

generic + dynamic(第二个例子)=&gt; 90毫秒

     

MulticastDelegate + DynamicInvoke(最初给出的问题)=&gt; 940毫秒

答案 2 :(得分:0)

您可以执行以下操作:

void DispatchEvent(MulticastDelegate handler, object sender, EventArgs args)
{
    EventHandler eventHandler = 
        (EventHandler)Delegate.CreateDelegate(typeof(EventHandler), handler.GetType().GetMethod("Invoke"));

    eventHandler(sender, args);
}

我不确定这是否比使用DynamicInvoke更快。

你必须在某处使用反射。如果每个代表都可以保证只有一个订阅者,那么您可以在创建EventHandler时直接使用Delegate.Method属性,但由于它们是事件,因此它们可能有多个订阅者...