The solution you need depends a bit on how the consumer of the Dispatcher call events. If the consumer always knows the exact type of event at compile time, you can use the generic Dispatch<TEvent>(TEvent) method, as shown above. In this case, the implementation of Dispatcher will be really simple.
If, on the other hand, consumers may not always know the exact type, but just work with the IEvent interface, the generic type argument in Dispatch<TEvent>(TEvent) becomes the useles symbol, and you better define the Dispatch(IEvent) method. This makes the implementation more complicated because you will need to use reflection to solve this problem.
Also note that it would be nice to introduce an IEventDispatcher abstraction. Do not call a static class from your code. Even Udi Dahan (who originally described such a static class a long time ago) now considers this anti-pattern. Instead, add an IEventDispatcher abstraction to classes that require event dispatching.
If all users work with event types that are known at compile time, your implementation will look like this:
public interface IEventDispatcher { void Dispatch<TEvent>(TEvent @event) where TEvent : IEvent; } private sealed class Dispatcher : IEventDispatcher { private readonly Container container; public Dispatcher(Container container) { this.container = container; } public void Dispatch<TEvent>(TEvent @event) where TEvent : IEvent { if (@event == null) throw new ArgumentNullException("event"); var handlers = this.container.GetAllInstances<IEventHandler<TEvent>>(); foreach (var handler in handlers) { handler.Handle(@event); } } }
If, on the other hand, the types of events are unknown, you can use the following code:
public interface IEventDispatcher { void Dispatch(IEvent @event); } private sealed class Dispatcher : IEventDispatcher { private readonly Container container; public Dispatcher(Container container) { this.container = container; } public void Dispatch(IEvent @event) { if (@event == null) throw new ArgumentNullException("event"); Type handlerType = typeof(IEventHandler<>).MakeGenericType(@event.GetType()); var handlers = this.container.GetAllInstances(handlerType); foreach (dynamic handler in handlers) { handler.Handle((dynamic)@event); } } }
Note that using a dynamic keyword has several non-obvious advantages over using the .NET API. For example, when calling a Handle handler method that uses a dynamic one, any exception thrown from the handle will automatically throw. When using MethodInfo.Invoke , on the other hand, the exception will be wrapped in a new exception. This complicates the catch and debugging.
Event handlers can be registered as follows:
container.RegisterCollection(typeof(IEventHandler<>), listOfAssembliesToSearch);