I have defined a class CommandProcessor<T>
as having T
derive from Command
and contain a default constructor:
public class CommandProcessor<T> : ICommandProcessor<T> where T : Command, new()
The Command
type itself also defines a default constructor and it implements an interface, ICommand
. The interface contains a method that expects an input parameter to be of type T
:
void Process(T command);
So I expect that I am able to define a class:
public class SpecificCommandProcessor : CommandProcessor<SpecificCommand>
it will work because SpecificCommand
inherits from Command
and also provides a default constructor.
All good so far.
But Visual Studio 2013 with C# 4.5 won't compile the following line:
CommandProcessor<Command> test = new SpecificCommandProcessor();
saying that it cannot convert the source type to the target type.
This means that I can also not do the following:
List<CommandProcessor<Command>> myList = new List<CommandProcessor<Command>>;
var toAdd = new SpecificCommandProcessor();
myList.Add(toAdd);
I have tried direct casting and safe casting, but none is accepted by the compiler. Yet it is obvious that SpecificCommandProcessor
is in fact a CommandProcessor<Command>
.
What am I missing here?
SpecificCommandProcessor
is NOT a CommandProcessor<Command>
, it's a CommandProcessor<SpecificCommand>
- and those things are different.
Take, for example, List<Animal>
and List<Sheep>
(with obvious inheritance).
List<Animal> animals = new List<Sheep>(); // if this were legal
animals.Add(new Wolf()); // what should this do?
What you can do, is leverage generic interface covariance and contravariance in C#. For example, IEnumerable<T>
is covariant, which means that this:
IEnumerable<Animal> animals = new List<Sheep>();
will actually work. That's because there is absolutely no way to add items to an IEnumerable after the fact, you can only get items from it, and the items you'll get will definitely be instances of Animal
.
It is actually defined using IEnumerable<out T>
, where out
means that the result will only be used as an output from the interface, so the value is ok if it's at least a T, or any inheritor.
What you might need to do is to create a covariant interface with
public interface ICommandProcessor<out T> where T : Command, new(){}
and have the CommandProcessor implement it:
public class CommandProcessor<T>:ICommandProcessor<T> where T : Command, new(){}
In that case, the code:
List<ICommandProcessor<Command>> myList = new List<ICommandProcessor<Command>>();
var toAdd = new SpecificCommandProcessor();
myList.Add(toAdd);
compiles and works (provided that the classes are indeed keeping the covariance promise)
To understand the logic behind why covariance cannot work in this case, consider two "specific" command processors:
public class CreateCommandProcessor : CommandProcessor<CreateCommand>
public class DeleteCommandProcessor : CommandProcessor<DeleteCommand>
Then imagine we do this:
CommandProcessor<Command> processor = new CreateCommandProcessor();
Now, as far as the compiler is concerned, processor
is an object that can process a command. Any command. So the following should be valid:
processor.Process(new DeleteCommand());
Except... it isn't valid. Because processor
can actually only process Create commands. This is a contradiction. This is why the assignment is invalid.
More generally, this is why a generic interface taking T
as a method parameter cannot be covariant.
It's not clear exactly how useful it is to have a list of objects that can all handle completely different inputs but if the idea is to create a command queue of sorts (or similar) consider creating something like a list of invocations instead. Something like:
// Note non-generic interface
public class CommandInvocation<T> : ICommandInvocation
{
public CommandInvocation<T>(T command, CommandProcessor<T> processor)
{
// Assign params to fields...
}
public void Invoke()
{
_processor.Process(_command);
}
}
Then you can do the following:
var invocations = new List<ICommandInvocation>();
invocations.Add(new CommandInvocation<CreateCommand>(createCommand,
new CreateCommandProcessor()));
invocations.Add(new CommandInvocation<DeleteCommand>(deleteCommand,
new DeleteCommandProcessor()));
Depending on your use case, you could take this a step further and create a CommandInvocationFactory
that injects some kind of processor resolver to give you the right processor for a given command type (so you don't have to pass the command processor explicitly each time), e.g.:
public ICommandInvocation Get<T>(Command<T> command)
{
var processor = _processorFactory.Get<T>();
return new CommandInvocation<T>(command, processor);
}
Then you can just do:
invocations.Add(_invokerFactory.Get(new CreateCommand()));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With