Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# generic type not assignable

Tags:

c#

generics

I want to write a CommandProcessor using generics. The idea being that a Command is issued via a single object (the CommandProcessor itself) that then identifies the Command Handlers that process the given command.

However, the following code doesn't compile, and I've not been able to understand why:

class GenericCommandProcessor : ICommandProcessor
{
    private readonly IDictionary<Type, IList<ICommandHandler<ICommand>>> _handlers = 
        new Dictionary<Type, IList<ICommandHandler<ICommand>>>();

    public void Register<TCommand>(ICommandHandler<TCommand> handler) 
        where TCommand : ICommand
    {
        IList<ICommandHandler<ICommand>> handlers = GetHandlers<TCommand>();
        handlers.Add(handler); // <-- This doesn't compile
    }

    public void Process<TCommand>(TCommand command)
        where TCommand : ICommand
    {
        IList<ICommandHandler<ICommand>> handlers = GetHandlers<TCommand>();

        foreach (var commandHandler in handlers)
        {
            commandHandler.Handle(command);
        }
    }

    private IList<ICommandHandler<ICommand>> GetHandlers<TCommand>()
    {
        Type commandType = typeof(TCommand);

        IList<ICommandHandler<ICommand>> handlers;
        if (!_handlers.TryGetValue(commandType, out handlers))
        {
            handlers = new List<ICommandHandler<ICommand>>();
            _handlers.Add(commandType, handlers);
        }
        return handlers;
    }
}

This is the line that doesn't compile:

handlers.Add(handler);

The compiler returns the following error:

cannot convert from 'GenericCommandHandlerTest.ICommandHandler<TCommand>' to 'GenericCommandHandlerTest.ICommandHandler<GenericCommandHandlerTest.ICommand>'

I would expect it to, because Register() has a generic constraint:

where TCommand : ICommand

I've avoided the issue by resolving the command handler list from IoC (Castle Windsor in my case) in favour of having the dictionary of lists of registered handlers, but I'd love to understand why, at the CLR level, this code doesn't compile. I think I just can't see the wood for the trees...

Many thanks in advance.

like image 963
Neil Barnwell Avatar asked Dec 28 '22 23:12

Neil Barnwell


1 Answers

Just change your method to this:

public void AddListItem(IListItem listItem)
{
    _items.Add(listItem);
}

No need to use generics here.

As others already said: Even without the change, your code compiles, so please update your sample code.

Update after you fixed your example:
You can't add a variable of type ICommandHandler<TCommand> to a IList<ICommandHandler<ICommand>>, because ICommandHandler<ICommand> and ICommandHandler<TCommand> are two different types, although TCommand implements ICommand. If it would work, my first answer would be correct again and you wouldn't need to make your method generic in the first place.

I guess Covariance would be helpful here, but unfortunately it is not supported in that case.

like image 144
Daniel Hilgarth Avatar answered Dec 30 '22 13:12

Daniel Hilgarth