I'm building a simple "bus" as a proof of concept. I do not need anything complicated but am wondering how best to optimise the following piece of code. I'm using Autofac as a container to resolve commands as open generics but actually executing the commands is currently being done via reflection as the incoming command cannot be cast to a concrete type in the code. See code - marked up with // BEGIN // END - this is currently being done with reflection. Is there a way to do this without using reflection?
// IoC wrapper
static class IoC {
public static object Resolve(Type t) {
// container gubbins - not relevant to rest of code.
}
}
// Handler interface
interface IHandles<T> {
void Handle(T command);
}
// Command interface
interface ICommand {
}
// Bus interface
interface IBus {
void Publish(ICommand cmd);
}
// Handler implementation
class ConcreteHandlerImpl : IHandles<HelloCommand> {
public void Handle(HelloCommand cmd) {
Console.WriteLine("Hello Command executed");
}
}
// Bus implementation
class BusImpl : IBus {
public void Publish(ICommand cmd) {
var cmdType = cmd.GetType();
var handler = IoC.Resolve(typeof(IHandles<>).MakeGenericType(cmdType));
// BEGIN SLOW
var method = handler.GetType().GetMethod("Handle", new [] { cmdType });
method.Invoke(handler, new[] { cmd });
// END SLOW
}
}
How about this (only the changed parts shown):-
// Handler interface
interface IHandles<T> where T : ICommand {
void Handle(T command);
}
// Bus interface
interface IBus {
void Publish<T>(T cmd) where T : ICommand;
}
// Bus implementation
class BusImpl : IBus {
public void Publish<T>(T cmd) where T : ICommand {
var handler = (IHandles<T>)IoC.Resolve(typeof(IHandles<T>));
handler.Handle(cmd);
}
}
The key here is to make the Publish
method generic, which means that you get a type reference T
to the type of the command, which can then be used to make the cast. The type parameter constraints simply ensure that only an ICommand
can be passed, as before.
BTW - I've tested this and it works, here's the full code:-
public static void Main(){
new BusImpl().Publish(new HelloCommand());
}
// IoC wrapper
static class IoC {
public static object Resolve(Type t) {
return new ConcreteHandlerImpl();
}
}
// Handler interface
interface IHandles<T> where T : ICommand {
void Handle(T command);
}
// Command interface
interface ICommand {
}
// Handler implementation
class ConcreteHandlerImpl : IHandles<HelloCommand> {
public void Handle(HelloCommand cmd) {
Console.WriteLine("Hello Command executed");
}
}
public class HelloCommand:ICommand{}
// Bus interface
interface IBus {
void Publish<T>(T cmd) where T : ICommand;
}
// Bus implementation
class BusImpl : IBus {
public void Publish<T>(T cmd) where T : ICommand {
var handler = (IHandles<T>)IoC.Resolve(typeof(IHandles<T>));
handler.Handle(cmd);
}
}
-- UPDATE --
As pointed out by Peter Lillevold, you should also think about adding a type parameter to your IOC container method as follows:-
// IoC wrapper
static class IoC {
public static T Resolve<T>() {
...
}
}
this will simplify your caller like so:-
// Bus implementation
class BusImpl : IBus {
public void Publish<T>(T cmd) where T : ICommand {
var handler = IoC.Resolve<IHandles<T>>();
handler.Handle(cmd);
}
}
This is a side point to your original question, but would seem a sensible design for the IOC interface.