Summary: in this tutorial, you’ll learn about the C# Command design pattern and how to use it to make your application more flexible and extensible.
Introduction to the C# command design pattern
The Command pattern is a behavioral design pattern that encapsulates a request as an object, thereby allowing the requester to be decoupled from the object that operates.
The Command pattern encapsulates the request as a command object that contains all the necessary information to execute the request.
The requester doesn’t need to know the details of how the command is executed. Instead, it simply requests the execution of the command by calling its execute method.
This makes your application more flexible and extensible because the requester can easily switch out different commands without the need of understanding how those commands are executed.
Because the command object stores the necessary information to reverse or replay the operation, the Command pattern enables features like undo and redo.
C# Command pattern structure
The following UML diagram illustrates the Command design pattern:
In this diagram:
ICommand
: Defines the interface for executing an operation.ConcreteCommand
: Implements theICommand
interface and defines the binding between aReceiver
object and an action. TheConcreteCommand
implements theExecute()
andUndo()
method by calling the corresponding operations on theReceiver
object.- Client: Creates
ConcreteCommand
objects and sets their receivers. - Receiver: Knows how to perform the operations associated with carrying out a request.
- Invoker: Asks the command to carry out the request.
Collaboration between participants:
- The
Client
creates aConcreteCommand
object and tells it which receiver object should receive the command. - An
Invoker
object saves theConcreteCommand
object. - The
Invoker
calls theExecute
method on theICommand
object to carry out the request. If the command is undoable, theConcreteCommand
object stores information needed to undo the command before callingExecute
method. - The
ConcreteCommand
object then directs theReceiver
to perform the necessary actions to fulfill the request.
The following shows how to implement the command pattern in C# (without Undo()
method, more on this in the next example):
namespace CommandDesignPattern;
public interface ICommand
{
void Execute();
}
public class Receiver
{
public void Action()
{
Console.WriteLine("Perform an action");
}
}
public class ConcreteCommand : ICommand
{
private Receiver Receiver
{
get; set;
}
public ConcreteCommand(Receiver receiver)
{
Receiver = receiver;
}
public void Execute() => Receiver.Action();
}
public class Invoker
{
public ICommand? Command
{
get; set;
}
public void Invoke() => Command?.Execute();
}
class Client
{
public static void Main(string[] args)
{
var receiver = new Receiver();
var concreteCommand = new ConcreteCommand(receiver);
var invoker = new Invoker
{
Command = concreteCommand
};
invoker.Invoke();
}
}
Code language: C# (cs)
C# Command pattern example
The following program defines a Calculator
class with methods for basic arithmetic operations including adding, subtracting, multiplying, and dividing that update the current value.
In the Main()
method of the Program
class, we create an instance of the Calculator
class, add 20 to the current value, subtract 5 from it, and then display the current value of the calculator in the console:
namespace CommandPattern;
public class Calculator
{
public double CurrentValue
{
get; private set;
}
public double Add(double valueToAdd) => CurrentValue += valueToAdd;
public double Subtract(double valueToSubtract) => CurrentValue -= valueToSubtract;
public double Divide(double valueToDivide) => CurrentValue /= valueToDivide;
public double Mutiply(double valueToMultiply) => CurrentValue *= valueToMultiply;
}
public class Program
{
public static void Run(string[] args)
{
var calculator = new Calculator();
calculator.Add(20);
calculator.Subtract(5);
Console.WriteLine(calculator.CurrentValue);
}
}
Code language: C# (cs)
Output:
15
Code language: C# (cs)
The program currently doesn’t support the undo feature because it is not using the Command pattern. To add the undo feature, we can refactor the program by applying the Command pattern:
namespace CommandPattern;
public interface ICommand
{
double Execute(double value);
double Undo(double value);
}
public class AddCommand : ICommand
{
private readonly double _valueToAdd;
public AddCommand(double valueToAdd)
{
_valueToAdd = valueToAdd;
}
public double Execute(double currentValue) => currentValue += _valueToAdd;
public double Undo(double currentValue) => currentValue -= _valueToAdd;
}
public class SubtractCommand : ICommand
{
private readonly double _valueToSubtract;
public SubtractCommand(double valueToSubtract)
{
_valueToSubtract = valueToSubtract;
}
public double Execute(double currentValue) => currentValue -= _valueToSubtract;
public double Undo(double currentValue) => currentValue += _valueToSubtract;
}
public class DivideCommand : ICommand
{
private readonly double _valueToDivide;
public DivideCommand(double valueToDivide)
{
_valueToDivide = valueToDivide;
}
public double Execute(double currentValue) => currentValue /= _valueToDivide;
public double Undo(double currentValue) => currentValue *= _valueToDivide;
}
public class MultiplyCommand : ICommand
{
private readonly double _valueToMultiply;
public MultiplyCommand(double valueToMultiply)
{
_valueToMultiply = valueToMultiply;
}
public double Execute(double currentValue) => currentValue *= _valueToMultiply;
public double Undo(double currentValue) => currentValue /= _valueToMultiply;
}
public class Calculator
{
public double CurrentValue
{
get; private set;
}
public Stack<ICommand> _commandHistory = new();
public void ExecuteCommand(ICommand command)
{
CurrentValue = command.Execute(CurrentValue);
_commandHistory.Push(command);
}
public void Undo()
{
var command = _commandHistory.Pop();
CurrentValue = command.Undo(CurrentValue);
}
}
public class Program
{
public static void Main(string[] args)
{
var calculator = new Calculator();
calculator.ExecuteCommand(new AddCommand(20));
calculator.ExecuteCommand(new SubtractCommand(10));
calculator.ExecuteCommand(new MultiplyCommand(5));
Console.WriteLine(calculator.CurrentValue);
calculator.Undo();
Console.WriteLine(calculator.CurrentValue);
}
}
Code language: C# (cs)
How it works.
First, create an ICommand
interface that has two methods Execute
and Undo
:
public interface ICommand
{
double Execute(double value);
double Undo(double value);
}
Code language: C# (cs)
Second, define the AddCommand
that implements the ICommand
interface. The AddCommand
adds a value to a current value:
public class AddCommand : ICommand
{
private readonly double _valueToAdd;
public AddCommand(double valueToAdd)
{
_valueToAdd = valueToAdd;
}
public double Execute(double currentValue) => currentValue += _valueToAdd;
public double Undo(double currentValue) => currentValue -= _valueToAdd;
}
Code language: C# (cs)
The Execute()
method adds a value to the current value. And the Undo()
method subtracts the same value from the current value.
Similarly, define the SubtractCommand
, MultiplyCommand
, and DivideCommand
classes:
public class SubtractCommand : ICommand
{
private readonly double _valueToSubtract;
public SubtractCommand(double valueToSubtract)
{
_valueToSubtract = valueToSubtract;
}
public double Execute(double currentValue) => currentValue -= _valueToSubtract;
public double Undo(double currentValue) => currentValue += _valueToSubtract;
}
public class DivideCommand : ICommand
{
private readonly double _valueToDivide;
public DivideCommand(double valueToDivide)
{
_valueToDivide = valueToDivide;
}
public double Execute(double currentValue) => currentValue /= _valueToDivide;
public double Undo(double currentValue) => currentValue *= _valueToDivide;
}
public class MultiplyCommand : ICommand
{
private readonly double _valueToMultiply;
public MultiplyCommand(double valueToMultiply)
{
_valueToMultiply = valueToMultiply;
}
public double Execute(double currentValue) => currentValue *= _valueToMultiply;
public double Undo(double currentValue) => currentValue /= _valueToMultiply;
}
Code language: C# (cs)
Third, define the Calculator
class that executes a command and undoes it:
public class Calculator
{
public double CurrentValue
{
get; private set;
}
public Stack<ICommand> _commandHistory = new();
public void ExecuteCommand(ICommand command)
{
CurrentValue = command.Execute(CurrentValue);
_commandHistory.Push(command);
}
public void Undo()
{
var command = _commandHistory.Pop();
CurrentValue = command.Undo(CurrentValue);
}
}
Code language: C# (cs)
The Calculator
class has a CurrentValue
property that represents its current value. Also, it has a private field _commandHistory
that keeps track of previously executed commands.
The ExecuteCommand
method takes an
object. The method calls the ICommand
Execute()
method of an
object with the current value of the calculator and then adds the ICommand
object to the command history stack.ICommand
The Undo
method pops the most recent command off of the command history stack, calls the Undo
method of the command with the current value of the calculator, and updates the CurrentValue
property with the result.
Finally, define the Program
class that demonstrates the ability to execute and undo commands using the Command pattern:
public class Program
{
public static void Main(string[] args)
{
var calculator = new Calculator();
calculator.ExecuteCommand(new AddCommand(20)); // -> 20
calculator.ExecuteCommand(new SubtractCommand(10)); // -> 10
calculator.ExecuteCommand(new MultiplyCommand(5)); // -> 50
Console.WriteLine(calculator.CurrentValue); // output 50
calculator.Undo(); // 10
Console.WriteLine(calculator.CurrentValue); // output 10
}
}
Code language: C# (cs)
Output:
50
10
Code language: C# (cs)
In the Main()
method:
- First, create a new instance of the
Calculator
class. - Next, execute three commands including
AddCommand
,SubtractCommand
, andMultiplyCommand
using theExecuteCommand
method of theCalculator
object with the appropriate arguments (20, 10, and 5, respectively). - Then, display the
CurrentValue
of theCalculator
object to the console. - After that, call the
Undo
method of theCalculator
object, which undoes the most recent command (theMultiplyCommand
in this case) and updates theCurrentValue
property of theCalculator
object with the result of the undo operation. - Finally, print the updated current value of the calculator to the console.
In this program, the following objects map to the Command pattern:
ICommand
interface: defines the common methods that all concrete commands should implement.AddCommand
,SubtractCommand
,MultiplyCommand
, andDivideCommand
classes: concrete commands that implement theICommand
interface and encapsulate the operations that can be executed on theCurrentValue
property of theCalculator
object.Calculator
class: the invoker that receives and stores the concrete commands. It executes the commands by calling theirExecute
methods, and undoes them by calling theirUndo
methods.Program
class: the client that creates aCalculator
object and executes the concrete commands on it. It also invokes theUndo
method of theCalculator
object to undo the last executed command.
Summary
- Use the command pattern to decouple the requester and receiver of a request by encapsulating the request as an object.