简体   繁体   中英

Generic covariant cast or cast to real type

Trying to build a CQRS solution, I have the following code trying to find a Handler and then invoke a Handle() method.

The code below works but it's annoying to use reflection when we know that all IHandleCommand<> have a Handle method, this could be resolved at compiletime, I belive!

Do I have to use dynamic in some way?

public void SendCommand(Command command)
{
    Type handlerType = typeof(IHandleCommand<>).MakeGenericType(command.GetType());
    object handler = container.Resolve(handlerType);
    handler.GetType().GetMethod("Handle").Invoke(handler, new object[] { command });
}

Here's the other types used above

public class Command {}

public class MyCommand : Command {}

public interface IHandleCommand<T>
{
void Handle(T command);
}

public class MyCommandHandler : IHandleCommand<MyCommand>
{
    public void Handle(MyCommand command)   {}
}

I made something similar to that where I used a container (StructureMap in my case) to get handler instances from.

Check out that question and its answers: StructureMap register generic types against all possible concrete implementations

I used Autofac to solve the problem.
Here is what I ended up with

Assuming this interface

public interface IHandleCommand<T> where T : Command
{
  void Handle(T command);
}

The Servicebus will call something like this

private readonly IComponentContext container;

public InProcessBus(IComponentContext container)
{
  this.container = container;
}

public void Send<T>(T command) where T : Command
{
  if (command == null) throw new ArgumentNullException("Command");
  container.Resolve<IHandleCommand<T>>().Handle(command);
}

and my Autofac CommandsHandlersModule

public class CommandsHandlersModule : Autofac.Module
{
  protected override void Load(ContainerBuilder builder)
  {
     builder.RegisterAssemblyTypes(typeof(CartCommandsHandler).Assembly)
       .AsClosedTypesOf(typeof(IHandleCommand<>))
       .SingleInstance();
   }
}

Than from your app you call

ContainerBuilder builder = new ContainerBuilder();
builder.RegisterModule(new CommandsHandlersModule());

After playing around I found out some solutions:

dynamic handler = container.Resolve(handlerType);
handler.Handle(command as dynamic);

But if I send command as dynamic to a generic method I will get Command as it's real type as T to do magic with. Also the call to Handle() can be resolved at compile time. Then there's no need for generic covariant casting, which turns out to be problem from the beginning.

public void SendCommand(Command command)
{
    Invoke(command as dynamic);
}

private void Invoke<T>(T command) where T : Command
{
    var handler = container.Resolve<IHandleCommand<T>>();
    handler.Handle(command);
}

It is really nice, but I won't go entirely for that solution since I don't registrate command handlers, i'll use this:

private void Invoke<T>(T command) where T : Command
{
    Type handlerType = CommandToHandlerType(command);
    var handler = (IHandleCommand<T>)container.Resolve(handlerType);
    handler.Handle(command);
}

CommandToHandlerType() just searches an assembly for a type implementing IHandleCommand of T

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM