Во-первых, извините за смутное название вопроса. Я не мог придумать более точный.
С учетом этих типов:
{ TCommand : ICommand }
«interface» «interface» /
+-----------+ +----------------------/----+
| ICommand | | ICommandHandler<TCommand> |
+-----------+ +---------------------------+
^ | Handle(command: TCommand) |
| +---------------------------+
| ^
| |
+------------+ +-------------------+
| FooCommand | | FooCommandHandler |
+------------+ +-------------------+
^
|
+-------------------+
| SpecialFooCommand |
+-------------------+
Я хотел бы написать метод Dispatch
, который принимает любую команду и отправляет ее соответствующему ICommandHandler<>
. Я подумал, что использование контейнера DI (Autofac) может значительно упростить отображение типа команды в обработчик команды:
void Dispatch<TCommand>(TCommand command) where TCommand : ICommand
{
var handler = autofacContainer.Resolve<ICommandHandler<TCommand>>();
handler.Handle(command);
}
Допустим, контейнер DI знает обо всех типах, показанных выше. Сейчас я звоню:
Dispatch(new SpecialFooCommand(…));
В действительности это приведет к тому, что Autofac выбросит ComponentNotRegisteredException
, поскольку ICommandHandler<SpecialFooCommand>
недоступно.
В идеале, однако, я бы все же хотел, чтобы SpecialFooCommand
обрабатывался доступным обработчиком команд, наиболее близким к соответствующему, т.е. на FooCommandHandler
в приведенном выше примере.
Можно ли настроить Autofac для этой цели, возможно, с помощью специального источника регистрации?
PS: Я понимаю, что может быть фундаментальная проблема со-/ контравариантности, мешающая (как в следующем примере), и что единственным решением может быть решение, которое не использует дженерики вообще ... но я бы хотел придерживаться дженериков, если это возможно.
ICommandHandler<FooCommand> fooHandler = new FooCommandHandler(…);
ICommandHandler<ICommand> handler = fooHandler;
// ^
// doesn't work, types are incompatible