I setup an event bus that simply passes a string for all event types. This worked well but now I want different event arguments for each event type. I don’t see a way to keeping a single collection of subscribers that all have different event arguments. I can use a base type for the event arguments, but then the event handlers are forced to use the base type and the subscribers has to cast the event arguments to the concrete type (which I don’t want). I basically have something like this:
public abstract class PresentationEvent
{
private readonly List<Action<IPresentationEventArgs>> _subscribers = new List<Action<IPresentationEventArgs>>();
public void Subscribe(Action<IPresentationEventArgs> action)
{
_subscribers.Add(action);
}
public void Publish(IPresentationEventArgs message)
{
foreach (var sub in _subscribers)
{
sub.Invoke(message);
}
}
}
public class MessageChangedEvent : PresentationEvent
{
}
public static class EventBus
{
private static readonly Dictionary<Type, PresentationEvent> _mapping = new Dictionary<Type, PresentationEvent>();
private static PresentationEvent GetPresentationEvent<T>() where T : PresentationEvent, new()
{
if (_mapping.ContainsKey(typeof(T)))
{
return _mapping[typeof(T)];
}
var presEvent = new T();
_mapping.Add(typeof(T), presEvent);
return presEvent;
}
public static void Subscribe<T>(Action<IPresentationEventArgs> action) where T: PresentationEvent, new()
{
var presEvent = GetPresentationEvent<T>();
presEvent.Subscribe(action);
}
public static void Publish<T>(IPresentationEventArgs args) where T : PresentationEvent, new()
{
var presEvent = GetPresentationEvent<T>();
presEvent.Publish(args);
}
}
But when handling the event, I am forced to do this:
private void OnMessageChanged(IPresentationEventArgs x)
{
// do cast here
}
instead of:
private void OnMessageChanged(MessageChangedEventArgs args)
{
label1.Text = args.Message;
}
Other than keeping some event dictionary with different lists for each event type, I am not sure how I would handle this. I know there are third party libraries out there but I’d prefer to write the code myself. I’ve also looked at similar questions and don’t find anything. If anyone has a suggestion on how to solve this problem or other recommendations it would be appreciated.
If you add another generic parameter you can have strongly typed events.
So a small test program to demonstrate how this could work:
You have the added overhead of calling subscribe and publish with 2 generic parameters, but on the other hand you can bind an event to specific eventArgs and consumers cannot pass any arbitrary eventArgs for a given event. They would need to match.
Here is a small optimisation. Rather than create your own list of actions, you could just add actions up and allow the power of a Multicast delegate to keep track of all the actions for you. For example:
Update
Here is one other way you can do the subscription. But no matter which approach you take, if you want statical linking and compile time checks then you will need to supply 2 type arguments.
With that in mind here is another way, but you don’t avoid having to specify 2 arguments.