Если вы добавите еще один универсальный параметр, вы можете иметь строго типизированные события.
public interface IPresentationEventArgs { }
public abstract class PresentationEvent<TPresentationEventArgs> where TPresentationEventArgs : IPresentationEventArgs
{
private readonly List<Action<TPresentationEventArgs>> _subscribers = new List<Action<TPresentationEventArgs>>();
public void Subscribe(Action<TPresentationEventArgs> action)
{
_subscribers.Add(action);
}
public void Publish(TPresentationEventArgs message)
{
foreach (var sub in _subscribers)
{
sub.Invoke(message);
}
}
}
public class MessageChangedEventArgs : IPresentationEventArgs
{
public string Message { get; set; }
}
public class MessageChangedEvent : PresentationEvent<MessageChangedEventArgs>
{
}
public static class EventBus
{
private static readonly Dictionary<Type, Func<Object>> _mapping = new Dictionary<Type, Func<Object>>();
private static T GetPresentationEvent<T, TArgs>()
where T : PresentationEvent<TArgs>, new()
where TArgs : IPresentationEventArgs
{
if (_mapping.ContainsKey(typeof(T)))
{
return _mapping[typeof(T)]() as T;
}
var presEvent = new T();
_mapping.Add(typeof(T), () => presEvent);
return presEvent;
}
public static void Subscribe<T, TArgs>(Action<TArgs> action) where T : PresentationEvent<TArgs>, new()
where TArgs : IPresentationEventArgs
{
var presEvent = GetPresentationEvent<T, TArgs>();
presEvent.Subscribe(action);
}
public static void Publish<T, TArgs>(TArgs args) where T : PresentationEvent<TArgs>, new()
where TArgs : IPresentationEventArgs
{
var presEvent = GetPresentationEvent<T, TArgs>();
presEvent.Publish(args);
}
}
Итак, небольшая тестовая программа для демонстрации того, как это может работать:
class Program
{
static void OnMessageChanged(MessageChangedEventArgs args)
{
Console.WriteLine(args.Message);
}
static void Main(string[] args)
{
EventBus.Subscribe<MessageChangedEvent, MessageChangedEventArgs>(OnMessageChanged);
EventBus.Publish<MessageChangedEvent, MessageChangedEventArgs>(new MessageChangedEventArgs{ Message = "Hello world."});
Console.ReadKey();
}
}
У вас есть дополнительные накладные расходы на вызов подписки и публикации с 2 общими параметрами, но с другой стороныВы можете привязать событие к определенному EventArgs, и потребители не могут передавать произвольные EventArgs для данного события.Им нужно будет соответствовать.
Вот небольшая оптимизация.Вместо того чтобы создавать свой собственный список действий, вы можете просто добавить действия и позволить делегату многоадресной рассылки отслеживать все действия за вас.Например:
public abstract class PresentationEvent<TPresentationEventArgs> where TPresentationEventArgs : IPresentationEventArgs
{
private Action<TPresentationEventArgs> _actions = args => { };
public void Subscribe(Action<TPresentationEventArgs> action)
{
_actions += action;
}
public void Publish(TPresentationEventArgs message)
{
_actions(message);
}
}
Обновление
Вот еще один способ подписки.Но независимо от того, какой подход вы выберете, если вам нужны статические ссылки и проверки времени компиляции, вам нужно будет предоставить аргументы 2 типов.
- 1 аргумент типа для указания типа события, на которое вы хотите подписаться.
- 1 аргумент типа для приведения метода, на который подписывается как действие, так как компилятор не может сделать вывод, что он являетсяслучай только из сигнатуры метода
Имея это в виду, здесь есть другой способ, но вам не нужно указывать 2 аргумента.
public static class IPresentationEventArgsExtensions
{
public static void SubscribeTo<TEvent, TArgs>(this TEvent target, Action<TArgs> action)
where TArgs : IPresentationEventArgs
where TEvent : PresentationEvent<TArgs>, new()
{
EventBus.Subscribe<TEvent, TArgs>(action);
}
}
// Use
Action<MessageChangedEventArgs> messageChangedMethod = OnMessageChanged; // The compiler cannot infer that OnMessageChanged is a Action<IPresentationEventArgs>
new MessageChangedEvent().SubscribeTo(messageChangedMethod);