Использование MulticastDelegate в качестве параметра, избегая DynamicInvoke
У меня есть 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 ответа
Вот мое решение без отражения. Он в основном реализует многоадресный делегат в виде списка. Меньше кода? Нет. Лучшая производительность? Я не знаю. Очиститель? Мех.
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);
}
Одной простой альтернативой является использование встроенных типов, таких как 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 мс
Вы могли бы сделать что-то вроде:
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
, но поскольку они являются событиями, у них может быть более одного подписчика...