I’ve got a number of classes designed to handle objects of specific types.
e.g.,
class FooHandler : Handler<Foo> {
void ProcessMessage(Foo foo);
}
where the handler interface might be defined something like this:
interface Handler<T> {
void ProcessMessage(T obj);
}
Now, I’d like to be able to use a dictionary of these handlers:
Dictionary<Type, Handler> handlers;
void ProcessMessage(object message) {
var handler = handlers[message.GetType()];
handler.ProcessMessage(handler);
}
However, C# doesn’t appear to allow me to use the Handler interface without specifying a type. C# also doesn’t allow me to declare interface Handler<out T> so I can’t use Handler<object> in the handlers declaration.
Even this doesn’t work:
Dictionary<Type, object> handlers;
void ProcessMessage(object message) {
dynamic handler = handlers[message.GetType()];
handler.ProcessMessage(message);
}
This seems to be solvable using reflection:
handler.GetType().GetMethod("ProcessMessage").Invoke(handler, new object[] { message });
And, of course, I could remove the generics from the Handler interface. However, the reason I went down this path is that I wanted to make the API for handlers as simple as possible. I wanted classes to specify the messages they receive and for them to be able to process those messages without having to cast the parameters in every method.
I’d prefer to avoid reflection if possible and avoiding generics altogether doesn’t quite seem satisfactory.
Am I missing something obvious or am I pushing up against the limits of C#’s generics?
I realize that C# isn’t Java (with Java’s type erasure, this would be easy) and perhaps this might have been better solved in a more C#-like way… so I’m also interested in other approaches.
Thanks!
There is a better way. Just embed the cast in a lambda and store the action rather than the handler:
OK, this goes beyond the scope of the question a bit, but the discussion in the comments led us here:
No strings involved… Of course, if you want to establish the convention by naming you could make the scanning easier and simply look up the handler type from the name of the message type as done in your comment. You could also replace the Activator.CreateInstance with an IOC container.