Interface covariance contravariance : why is this not compiling?

Viewed 348

I want to implement a CommandBus that can Dispatch some Commands to CommandHandlers.

  • A Command is a simple a DTO describing what should happen. For instance : "Increment counter by 5"
  • A CommandHandler is able to handle a precise type of Command.
  • The CommandBus takes a Command and executes the CommandHandler that is able to handle it.

The code I wrote does not compile.

Compiler complains cannot convert from 'IncrementHandler' to 'Handler<Command>'. I don't understand why, because IncrementHandler implements Handler<Increment> and Increment implements Command

I've tried both in and out modifiers on the generic interfaces, it doesn't solve the problem.

Is there a way to achieve this with only interfaces ?

[TestClass]
public class CommandBusTest
{
  [TestMethod]
  public void DispatchesProperly()
  {
    var handler = new IncrementHandler(counter: 0);
    var bus = new CommandBus(handler); // <--Doesn't compile: cannot convert from 'IncrementHandler' to 'Handler<Command>'
    bus.Dispatch(new Increment(5));
    Assert.AreEqual(5, handler.Counter);
  }
}

public class CommandBus
{
  private readonly Dictionary<Type, Handler<Command>> handlers;

  public CommandBus(params Handler<Command>[] handlers)
  {
    this.handlers = handlers.ToDictionary(
      h => h.HandledCommand,
      h => h);
  }

  public void Dispatch(Command commande) { /*...*/ }
}

public interface Command { }

public interface Handler<TCommand> where TCommand : Command
{
  Type HandledCommand { get; }
  void Handle(TCommand command);
}

public class Increment : Command
{
  public Increment(int value) { Value = value; }

  public int Value { get; }
}

public class IncrementHandler : Handler<Increment>
{
  // Handler<Increment>
  public Type HandledCommand => typeof(Increment);
  public void Handle(Increment command)
  {
    Counter += command.Value;
  }
  // Handler<Increment>

  public int Counter { get; private set; }

  public IncrementHandler(int counter)
  {
    Counter = counter;
  }
}
3 Answers
Related