Использование MulticastDelegate в качестве параметра, избегая DynamicInvoke - PullRequest
3 голосов
/ 20 января 2011

У меня есть 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 ]

2 голосов
/ 21 января 2011

Вот мое решение без отражений.Он в основном реализует многоадресный делегат в виде списка.Меньше кода?Нет. Лучшая производительность?Я не знаю.Очиститель?Мех.

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 голос
/ 04 июня 2013

Одной простой альтернативой является использование встроенных типов, таких как 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 (первый пример) => 40 мс

generic + dynamic (второй пример) => 90 мс

MulticastDelegate + DynamicInvoke (задано изначально) => 940 мс

0 голосов
/ 20 января 2011

Вы можете сделать что-то вроде:

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

    eventHandler(sender, args);
}

Я не уверен, что это будет быстрее, чем при использовании DynamicInvoke.

Вам нужно будет где-нибудь использовать отражение.Если у каждого делегата может быть гарантирован только один подписчик, то вы можете использовать свойство Delegate.Method напрямую при создании EventHandler, но поскольку они являются событиями, они могут иметь болееодин подписчик ...

...