Reputation: 9648
I'm working on a system for handling events:
public interface IEvent { ..}
public class CreateUserEvent : IEvent {...}
public class ChangeUserNameEvent : IEvent {...}
Each event has a specific handler
public interface IEventHandler<T> where T : IEvent { Handle(T @event); }
public class CreateUserEventHandler : IEventHandler<CreateUserEvent> { ... }
public class ChangeUserNameEventHandler : IEventHandler<ChangeUserNameEvent> {...}
So far everything is pretty straight forward. However, I would like to create class that uses the right event handler, for the right event.
So far I've come up with the following method:
Dictionary<Type, object> EventHandlers; // stores all registered event handlers
// Note that at compile time I do not exactly know the specialization of IEvent
// so I cannot give HandleEvent a generic type parameter :(
void HandleEvent(IEvent @event)
{
// inspect the type of the event handler at runtime
// the event also needs to be dynamic. Even though we know its a
// specialization of IEvent that is compatible with
// the handlers .Handle method
var handler = EventHandlers[@event.GetType()] as dynamic;
hanler.Handle(@event as dynamic);
}
This solution works, but I have to use two dynamic types, this worries me. I think I might be making a wrong design decision, but I can think of no other architecture/pattern to get rid of these dynamics.
So my question boils down to this: How can I select and use the right implemenation of an interface with a generic with minimal run-time introspection?
Note I prefer a solution where IEvent and IEventHandler implementations are completely unaware of this process
Upvotes: 2
Views: 609
Reputation: 2262
I would try something loosely based on the Subject<T> and the OfType extension method in Rx.NET. This would delay type checking until the last moment, so you might want to rewrite it to a dictionary based solution. Also this code is in no way thread safe, use the Rx.NET code as a reference to improve it in multi-threading usage cases.
The biggest problem with this solution is that the type of the handler is hidden in the call to the EventDispatcher.Dispatch method. In the question you state that you want a non-generic method which has no compile-time knowledge about the event to dispatch.
public interface IEvent
{
}
public interface IEventHandler<TEvent> where TEvent: IEvent
{
void Handle<TEvent>(TEvent message)
}
public class EventDispatcher
{
private List<object> handlers = new List<object>();
public void Dispatch<TEvent>(TEvent message)
{
foreach (var handler in handlers)
{
if (handler is IEventHandler<TEvent>)
{
var safeHandler = (IEventHandler<TEvent>)handler;
safeHandler.Handle(message);
}
}
}
public IDisposable Register<TEvent>(IEventHandler<TEvent> handler)
{
this.handlers.Add(handler);
return new Subscription(this, handler);
}
class Subscription : IDisposable
{
private EventDispatcher dispatcher;
private IEventHandler<TEvent> handler;
public Subscription(EventDispatcher dispatcher, IEventHandler<TEvent> handler)
{
this.dispatcher = dispatcher;
this.handler = handler;
}
public void Dispose()
{
if (dispatcher == null)
return;
dispatcher.Unsubscribe(handler);
dispatcher = null;
}
}
private void Unsubscribe(IEventHandler<TEvent> handler)
{
this.handlers.Remove(handler);
}
}
Upvotes: 1