I want to create a generic mechanism for handling messages in C#. I have a need for this in my small application, so I don't want to use full blown message bus. My requirements are quite simple:
- I want to have a couple of classes for messages i.e.
Message1
,Message2
. They can inherit from one base class, that's not a problem, but if they don't I don't care. Currently they do inherit fromMessage
. be able to get handler for each and every message class. i.e. if I send
Message1
, thenMessage1Handler
class should be instantiated. Handlers have to implementIMessageHandler<T>
whereT
is the message class.IMessageHandler
is defined as follows:interface IMessageHandler<T> { void Execute(T message); }
I wrote a simple "Resolver" class:
public static class HandlerRegistry
{
private static readonly Dictionary<string, Type> _handlers = new Dictionary<string, Type>();
public static void Register<T, T2>() where T2: IMessageHandler<T>
{
_handlers.Add(typeof(T).FullName, typeof(T2));
}
public static IMessageHandler<T> Resolve<T>(T parameters)
{
var type = _handlers[parameters.GetType().FullName];
return (IMessageHandler<T>) Activator.CreateInstance(type);
}
}
In this implementation everything is OK, but one part - the cast to IMessageHandler. When I'm trying to use this with a collection of messages this is what happens: the compiler doesn't know at compile time what actual messages are going to be in the collection - it just assumes that they are all subclasses of Message
, so it's trying to cast IMessageHandler<ConcreteMessage>
to IMessageHandler<Message>
and obviously I'm getting an exception with invalid cast. In this case probably contravariance would help, but I'm not able to declare the parameter as out because I have the message in the Execute method parameters.
Does anyone know an elegant solution to this problem? I know I can make it "more runtime" - instead of using generics just declare
void Execute(Message m)
and in each and every handler start with trying to cast to the type that I'm expecting, but as someone said somewhere - each and every cast that you write undermines the whole point of using a type system.