Это решение было первоначально опубликовано cdhowie на мой вопрос: Преобразование делегата нарушает равенство и не может отключиться от события , но, похоже, решает проблему ковариации и контравариантности в контексте многоадресных делегатов.
Сначала вам понадобится вспомогательный метод:
public static class DelegateExtensions
{
public static Delegate ConvertTo(this Delegate self, Type type)
{
if (type == null) { throw new ArgumentNullException("type"); }
if (self == null) { return null; }
if (self.GetType() == type)
return self;
return Delegate.Combine(
self.GetInvocationList()
.Select(i => Delegate.CreateDelegate(type, i.Target, i.Method))
.ToArray());
}
public static T ConvertTo<T>(this Delegate self)
{
return (T)(object)self.ConvertTo(typeof(T));
}
}
Когда у вас есть делегат:
public delegate MyEventHandler<in T>(T arg);
Вы можете использовать его при объединении делегатов, просто преобразовав делегат в нужный тип:
MyEventHandler<MyClass> handler = null;
handler += new MyEventHandler<MyClass>(c => Console.WriteLine(c)).ConvertTo<MyEventHandler<MyClass>>();
handler += new MyEventHandler<object>(c => Console.WriteLine(c)).ConvertTo<MyEventHandler<MyClass>>();
handler(new MyClass());
Он также поддерживает отключение от события, используя метод ConvertTo()
.
В отличие от использования некоторого пользовательского списка делегатов, это решение обеспечивает безопасность потоков из коробки.
Полный код с некоторыми примерами вы можете найти здесь: http://ideone.com/O6YcdI