0

When I was looking this question, a question comes my mind.

Think about using an interface like :

public interface ICommandProcessor<T> where T : ICommand
{
    void Process(T command);
}

When you think about just interfaces, is there any advantage over direct using? Like :

public interface ICommandProcessor
{
    void Process(ICommand command);
}

Edit:

@Olivier's answer hits a good point.

It is an advantage if you want to create a command processor for a specialized command type

If specialized command type is needed, it is good. But I am not sure to force all command classes to use generics because of maybe they would need specialized command type. So, when think both things :

public interface ICommandProcessor<in T> where T : ICommand
{
    void Process(T command);
}

public interface ICommandProcessor : ICommandProcessor<ICommand>
{

}

public class SampleClass : ICommandProcessor
{
    public void Process(ICommand command)
    {
        //..
    }
}

public class AnotherSampleClass : ICommandProcessor<IAnotherCommand>
{
    public void Process(IAnotherCommand command)
    {
        //..
    }
}

Can we say it is the best design for ICommandProcessor? By this way, classes can implement ICommandProcessor or ICommandProcessor<T> if needs to create special one, I think.

  • The advantages are the same as those you get by using generic typing elsewhere (i.e. not in an interface). The interface has its own benefits, but those benefits are independent from the ones you obtain from generic typing. – Robert Harvey Aug 22 at 16:39
  • So the question is not "why use generics in interfaces," it's why use generics. – Robert Harvey Aug 22 at 16:42
  • Anyway, the advantage of your first code sample over your second one is that T is constrained to ICommand at compile time, so if you get your concrete types wrong, you won't have to wait until your program blows up unexpectedly at run time to find that out. – Robert Harvey Aug 22 at 16:55
  • @RobertHarvey Isn't it same as second usage? Concrete types should implements ICommand. – Engineert Aug 22 at 16:58
  • 1
    In other words, the generic types and constraints actually increase your options, not decrease them, while also guaranteeing type safety at compile time. – Robert Harvey Aug 22 at 17:05
7

It is an advantage if you want to create a command processor for a specialized command type. E.g. you have an interface

public interface IUndoableCommand : ICommand
{
    void Undo();
}

Now you can use it like this:

public class UndoableCommandProcessor : ICommandProcessor<IUndoableCommand>
{
    public void Process(IUndoableCommand command)
    {
        //TODO: Execute command. Push command to undo stack.
    }
}

UndoableCommandProcessor.Process() will not accept a simple ICommand.


The generic variant allows you to create specialized command processors restricted to process only specialized commands.

The non-generic variant allows you to create specialized commands and pass them to the processor, but the command processor must always be able to process the general ICommand.


A disadvantage of generic types is, that they are not assignment compatible when specialized with different generic type parameters. So T<A> and T<B> are not assignment compatible, even if A and B are. (Exception, see: Creating Variant Generic Interfaces (C#)).

Therefore, it is often useful for generic types to have a non-generic base type.

public interface ICommandProcessor
{
    void Process(ICommand command);
}

public interface ICommandProcessor<T> : ICommandProcessor
    where T : ICommand
{
    void Process(T command);
}

So, this is the exact contrary of your approach public interface ICommandProcessor : ICommandProcessor<ICommand>.

An implementation would hide the non-generic Process method by implementing it explicitly. This makes it accessible only when called directly through the ICommandProcessor interface.

If we have

public interface ICommand { }
public interface ICommandA : ICommand { }
public interface ICommandB : ICommand { }

Then we can create a specialized implementation like this:

public class CommandProcessorA : ICommandProcessor<ICommandA>
{
    void ICommandProcessor.Process(ICommand command) // Explicit implementation
    {
    }

    public void Process(ICommandA command) // Implicit implementation
    {
    }
}

We declare public class CommandProcessorB : ICommandProcessor<ICommandB> the same way. Now we can store different command processors like this

var processors = new List<ICommandProcessor> {
    new CommandProcessorA(),
    new CommandProcessorB()
};

This does not work:

// DOES NOT COMPILE!
var processors = new List<ICommandProcessor<ICommand>> {
    new CommandProcessorA(),
    new CommandProcessorB()
};

We get

Error CS1950 The best overloaded Add method 'List<ICommandProcessor<ICommand>>.Add(ICommandProcessor<ICommand>)' for the collection initializer has some invalid arguments ...

0

Building further on Olivers answer. Its also alot easier to get a factory going with a generic interface version. You just do

public class CommandRunner
{
   public CommandRunner(IServiceProvider serviceProvider) 
   {
       _serviceProvider = serviceProvider;
   }

   public void Execute<TCOmmand>(TCommand cmd) where TCommand : ICommand
   {
      _serviceProvider.GetService<ICommandHandler<TCommand>>().Execute(cmd);
   }
}
0

You can only tell what the best design is after you established your usage scenario.

ICommandProcessor would work with any command whereas ICommandProcessor<T> would only work with a specific type of command. The first will give you more flexibility (it is more generic than the generic interface, go figure). The latter gives you stronger type safety.

What is more important to your application? Do you want to toss around commands that your main logic does not need to know the details about? Or do you discriminate between command types in your main logic?

It won't hurt to declare both interfaces. After all, it only starts to matter once a class implements either. And you can always implement both but that may not be helpful to the user of your class. Choosing one sends a message about the intended usage scenario. That is what you should consider: what is a command to me (or to my application). The choice between the two interfaces should follow.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.