Command Pattern : How to pass parameters to a command?

C#Design PatternsCommand Pattern

C# Problem Overview


My question is related to the command pattern, where we have the following abstraction (C# code) :

public interface ICommand
{
    void Execute();
}

Let's take a simple concrete command, which aims to delete an entity from our application. A Person instance, for example.

I'll have a DeletePersonCommand, which implements ICommand. This command needs the Person to delete as a parameter, in order to delete it when Execute method is called.

What is the best way to manage parametrized commands ? How to pass parameters to commands, before executing them ?

C# Solutions


Solution 1 - C#

You'll need to associate the parameters with the command object, either by constructor or setter injection (or equivalent). Perhaps something like this:

public class DeletePersonCommand: ICommand
{
     private Person personToDelete;
     public DeletePersonCommand(Person personToDelete)
     {
         this.personToDelete = personToDelete;
     }

     public void Execute()
     {
        doSomethingWith(personToDelete);
     }
}

Solution 2 - C#

Passing the data in via a constructor or setter works, but requires the creator of the command to know the data the command needs...

The "context" idea is really good, and I was working on (an internal) framework that leveraged it a while back.

If you set up your controller (UI components that interact with the user, CLI interpreting user commands, servlet interpreting incoming parameters and session data, etc) to provide named access to the available data, commands can directly ask for the data they want.

I really like the separation a setup like this allows. Think about layering as follows:

User Interface (GUI controls, CLI, etc)
    |
[syncs with/gets data]
    V
Controller / Presentation Model
    |                    ^
[executes]               |
    V                    |
Commands --------> [gets data by name]
    |
[updates]
    V
Domain Model

If you do this "right", the same commands and presentation model can be used with any type of user interface.

Taking this a step further, the "controller" in the above is pretty generic. The UI controls only need to know the name of the command they'll invoke -- they (or the controller) don't need to have any knowledge of how to create that command or what data that command needs. That's the real advantage here.

For example, you could hold the name of the command to execute in a Map. Whenever the component is "triggered" (usually an actionPerformed), the controller looks up the command name, instantiates it, calls execute, and pushes it on the undo stack (if you use one).

Solution 3 - C#

There are some options:

You could pass parameters by properties or constructor.

Other option could be:

interface ICommand<T>
{
    void Execute(T args);
}

And encapsulate all command parameters in a value object.

Solution 4 - C#

My implementation would be this (using the ICommand proposed by Juanma):

public class DeletePersonCommand: ICommand<Person>
{
	public DeletePersonCommand(IPersonService personService)
	{  
		this.personService = personService;
	}
		
	public void Execute(Person person)
	{
		this.personService.DeletePerson(person);
	}
}

IPersonService could be an IPersonRepository, it depends in what "layer" your command is.

Solution 5 - C#

Pass the person when you create the command object:

ICommand command = new DeletePersonCommand(person);

so that when you execute the command, it already knows everything that it needs to know.

class DeletePersonCommand : ICommand
{
   private Person person;
   public DeletePersonCommand(Person person)
   {
      this.person = person;
   }

   public void Execute()
   {
      RealDelete(person);
   }
}

Solution 6 - C#

In this case, what we've done with our Command objects is to create a Context object which is essentially a map. The map contains name value pairs where the keys are constants and the values are parameters that are used by the Command implementations. Especially useful if you have a Chain of Commands where later commands depend on context changes from earlier commands.

So the actual method becomes

void execute(Context ctx);

Solution 7 - C#

In the constructor and stored as fields.

You will also want to eventually make your ICommands serializable for the undo stack or file persistence.

Solution 8 - C#

Based on the pattern in C#/WPF the ICommand Interface (System.Windows.Input.ICommand) is defined to take an object as a parameter on the Execute, as well as the CanExecute method.

interface ICommand
            {
                bool CanExecute(object parameter);
                void Execute(object parameter);
            }

This allows you to define your command as a static public field which is an instance of your custom command object that implements ICommand.

public static ICommand DeleteCommand = new DeleteCommandInstance();

In this way the relevant object, in your case a person, is passed in when execute is called. The Execute method can then cast the object and call the Delete() method.

public void Execute(object parameter)
            {
                person target = (person)parameter;
                target.Delete();
            } 

Solution 9 - C#

Already mentioned code from Blair Conrad(don't know how to tag him) works perfectly fine if you know what person you want to delete when you instantiate the class and his method would suffice.But,if you don't know who you gonna delete until you press the button you can instantiate the command using a method reference that returns the person.

   class DeletePersonCommand implements ICommand
{
     private Supplier<Person> personSupplier;

     public DeletePersonCommand(Supplier<Person> personSupplier)
     {
         this.personSupplier = personSupplier;
     }

     public void Execute()
     {
        personSupplier.get().delete();
     }
}

That way when the command is executed the supplier fetches the person you want to delete,doing so at the point of execution. Up until that time the command had no information of who to delete.

Usefull link on the supplier.

NOTE:code writen in java . Someone with c# knowledge can tune that.

Solution 10 - C#

You should create a CommandArgs object to contain the parameters you want to use. Inject the CommandArgs object using the constructor of the Command object.

Solution 11 - C#

DeletePersonCommand can have parameter in its constructor or methods . DeletePersonCommand will have the Execute() and in the execute can check attribute that will be passed by Getter/Setter previously the call of the Execute().

Solution 12 - C#

I would add any necessary arguments to the constructor of DeletePersonCommand. Then, when Execute() is called, those parameters passed into the object at construction time are used.

Solution 13 - C#

Have "Person" implement some sort of IDeletable interface, then make the command take whatever base class or interface your entities use. That way, you can make a DeleteCommand, which tries to cast the entity to an IDeletable, and if that works, call .Delete

public class DeleteCommand : ICommand
{
   public void Execute(Entity entity)
   {
      IDeletable del = entity as IDeletable;
      if (del != null) del.Delete();
   }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRomain VerdierView Question on Stackoverflow
Solution 1 - C#Blair ConradView Answer on Stackoverflow
Solution 2 - C#Scott StanchfieldView Answer on Stackoverflow
Solution 3 - C#JuanmaView Answer on Stackoverflow
Solution 4 - C#bloparodView Answer on Stackoverflow
Solution 5 - C#jopView Answer on Stackoverflow
Solution 6 - C#user12786View Answer on Stackoverflow
Solution 7 - C#Frank KruegerView Answer on Stackoverflow
Solution 8 - C#TheZenkerView Answer on Stackoverflow
Solution 9 - C#Kostas ThanasisView Answer on Stackoverflow
Solution 10 - C#David RobbinsView Answer on Stackoverflow
Solution 11 - C#Patrick DesjardinsView Answer on Stackoverflow
Solution 12 - C#Matt DillardView Answer on Stackoverflow
Solution 13 - C#Joel MartinezView Answer on Stackoverflow