2011-01-20 5 views
3

У меня есть MulticastDelegate, который может ссылаться на одного из нескольких (устаревших) делегатов, имеющих одну и ту же подпись. Например:Использование MulticastDelegate в качестве параметра, избегая DynamicInvoke

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?

+1

Время для этого устаревшего кода для обновления до EventHandler. До тех пор, нет. –

+0

Настоящий код напрямую не использует 'EventArgs', но использует собственный подкласс. Однако я не вижу причин, по которым он не должен использовать один и тот же делегат для каждого из отправленных событий - тогда я могу изменить его из MulticastDelegate на тип делегирования. –

ответ

2

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

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

Ну, это не краткость, но это позволяет избежать отражения и динамического вызова и научить меня чему-то новому, поэтому спасибо! –

+0

+1 для некоторых инноваций есть, но помогает ли это отправлять отдельные события? Я думаю, можно вызвать 'DispatchEvent (ObjectWasCreated.Invoke)' .. – nawfal

0

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

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

    eventHandler(sender, args); 
} 

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

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

+0

-1. Как это помогает ОП? У него нет типа «EventHandler», а не пользовательских делегатов. – nawfal

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 + динамический (первый пример) => 40 мс

родовое + динамический (второй пример) => 90 мс

MulticastDelegate + DynamicInvoke (даны в вопросе изначально) => 940 мс

+1

Спасибо за идеи и показатели производительности. Я бы не ожидал, что это будет так хорошо. –

Смежные вопросы