CRG
CRG

Reputation: 727

How to resolve generic type at runtime

I'm trying to build a command processor that can take any command that implements a marker interface (or maybe descends from a base class). The processor will handle the command that it is asked to process. However I'm struggling with resolving the true generic type as Resolve(Type) returns an object. I'm not sure is how to cast this if at all possible?

public void Process(ICommand command)
{
    var c = command.GetType();
    var t = typeof(ICommandHandler<>).MakeGenericType(new[] { c });
    var o = container.Resolve(t);
    //((ICommandHandler)o).Handle(command);  *** This doesn't work
}

The calling code would be something like this -

Dispatcher.Process(new SomeCommand(Guid.NewGuid(),"Param1",12345));

Upvotes: 2

Views: 1794

Answers (2)

Travis Illig
Travis Illig

Reputation: 23894

If you absolutely have to call the ICommandHandler<T>.Handle method and you have no other control over the design of the system, then reflection may be your only choice. There's no great way to deal with the switch from generic to non-generic.

Otherwise, you may have a couple of options.

First, if your Dispatcher.Process can be made generic, you can save all the casting.

public static class Dispatcher
{
  public static void Process<T>(T command) where T : ICommand
  {
    var handler = container.Resolve<ICommandHandler<T>>();
    handler.Handle(command);
  }
}

This is a pretty common solution to a problem like this that I've seen out in the wild.

If you can't do that, then you may be able to make your ICommandHandler<T> interface implement a non-generic ICommandHandler base interface.

public interface ICommandHandler
{
  void Handle(ICommand command);
}

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

In this latter case you'd have to switch your strongly-typed command handler implementations to call the same internal logic for generic or basic handling or you'll get different handling based on the call, which would be bad:

public class SomeCommandHandler : ICommandHandler<SomeCommand>
{
  public void Handle(ICommand command)
  {
    var castCommand = command as SomeCommand;
    if(castCommand == null)
    {
      throw new NotSupportedException("Wrong command type.");
    }
    // Hand off to the strongly-typed version.
    this.Handle(castCommand);
  }

  public void Handle(SomeCommand command)
  {
    // Here's the actual handling logic.
  }
}

Then when you resolve the strongly-typed ICommandHandler<T> your cast down to ICommandHandler (as shown in your question's sample code) will work.

This is also a pretty common solution, but I've seen it more in systems that existed before generics were available where an updated API was being added.

However, in all cases here, the problem really isn't that Autofac is returning an object; it's a class/type design problem that affects any generic-to-non-generic conversion scenario.

Upvotes: 3

CRG
CRG

Reputation: 727

Using Reflection - but is this the best way to approach this?

    public void Process(Command command)
    {
        var c = command.GetType();
        var ot = typeof(ICommandHandler<>);
        var type = ot.MakeGenericType(new[] { c });
        var mi = type.GetMethod("Handle");
        var o = container.Resolve(type);
        mi.Invoke(o, new object[] { command });
    }

Upvotes: 2

Related Questions