Command vs Strategy Pattern in C#: Key Differences Explained
Both the command pattern and the strategy pattern are behavioral design patterns from the Gang of Four catalog, and developers regularly confuse them. On the surface they look similar -- both encapsulate behavior behind an interface and let you swap implementations at runtime. But when you compare command vs strategy pattern in C# closely, meaningful differences emerge around intent, state management, undo capabilities, and execution timing. Understanding these differences is the key to choosing the right pattern for a given problem.
In this article, we'll break down each pattern's core intent, walk through side-by-side C# code examples that solve the same problem with each approach, and cover the practical criteria you should use when deciding between command vs strategy. We'll also look at how to combine both patterns, highlight common mistakes developers make with the command vs strategy decision, and wrap up with a detailed FAQ section.
Quick Overview: Command vs Strategy Foundations
Before diving into the command vs strategy comparison, let's establish what each pattern does on its own.
The Command Pattern
The command pattern encapsulates a request as an object. That object contains everything needed to perform the action -- the receiver, the method to invoke, and any parameters. This makes it possible to parameterize clients with different requests, queue operations, log changes, and support undo/redo functionality. The command pattern turns a method call into a first-class object that you can store, pass around, and execute later.
Think of it like writing a work order. The work order captures exactly what needs to happen, who should do it, and what materials are required. You hand it off, and someone else can execute it at any time -- or even reverse it. Understanding this "request as object" concept is essential to grasping command vs strategy differences.
The Strategy Pattern
The strategy pattern encapsulates an algorithm behind an interface and lets clients swap algorithms at runtime without changing the consuming code. The pattern focuses on how something is done rather than what is requested. Strategies are typically stateless -- they receive input, process it, and return output without retaining information about previous executions.
Think of it like choosing a route on a GPS. The destination stays the same, but you pick different algorithms -- fastest, shortest, avoid tolls -- depending on your priorities.
Side-by-Side Command vs Strategy Comparison
Here's a direct comparison of the command vs strategy pattern across the dimensions that matter most in practice.
Intent
The command pattern's intent is to encapsulate a request as an object. It represents an action -- something to be done. The strategy pattern's intent is to encapsulate an algorithm and make it interchangeable. It represents a way of doing something.
This distinction matters when evaluating command vs strategy for a given problem. A command says "do this specific thing." A strategy says "here's how to do a category of things."
State
Commands carry state. A command object stores everything needed to execute the action -- the receiver reference, parameter values, and often a snapshot of previous state for undo support. This makes commands self-contained and replayable.
Strategies are stateless. They receive their input through method parameters at execution time. A strategy doesn't know or care about previous invocations. It's a pure algorithm -- input in, output out.
Undo Capability
The command pattern naturally supports undo. Because each command object captures both the action and enough state to reverse it, you can maintain a history stack and walk backwards through it. This is a core strength of the command vs strategy approach.
The strategy pattern doesn't support undo. Strategies are fire-and-forget from a pattern perspective. There's no built-in mechanism for reversing the effect of a strategy execution because the strategy doesn't track what it changed.
Execution Timing
Commands can be deferred, queued, scheduled, and batched. Because the command is a self-contained object, you can create it now and execute it later -- or hand it to a different thread, process, or service entirely. This decoupling of creation and execution is one of the most important command vs strategy differences and a defining feature of the command pattern.
Strategies execute immediately. When the client selects a strategy and invokes it, the algorithm runs right away. There's no concept of storing a strategy invocation for later execution -- that would be turning it into a command. This timing difference is another critical dimension of the command vs strategy analysis.
C# Code Examples: Command vs Strategy for Text Formatting
Let's make the command vs strategy comparison concrete with a real example. We'll build a text formatting system -- first with the strategy pattern, then with the command pattern -- so you can see how the same problem looks under each approach.
Strategy Pattern Approach
With the strategy pattern, we define a formatting algorithm interface and swap implementations at runtime:
public interface ITextFormatter
{
string Format(string input);
}
public sealed class UpperCaseFormatter : ITextFormatter
{
public string Format(string input)
{
return input.ToUpperInvariant();
}
}
public sealed class TrimFormatter : ITextFormatter
{
public string Format(string input)
{
return input.Trim();
}
}
public sealed class ReverseFormatter : ITextFormatter
{
public string Format(string input)
{
char[] chars = input.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}
}
The context class accepts any formatter strategy through dependency injection:
public sealed class TextProcessor
{
private ITextFormatter _formatter;
public TextProcessor(ITextFormatter formatter)
{
_formatter = formatter;
}
public void SetFormatter(ITextFormatter formatter)
{
_formatter = formatter;
}
public string Process(string text)
{
return _formatter.Format(text);
}
}
Usage is straightforward. You pick a strategy, call Process, and get the result immediately:
var processor = new TextProcessor(
new UpperCaseFormatter());
string result = processor.Process(" hello world ");
// result: " HELLO WORLD "
processor.SetFormatter(new TrimFormatter());
result = processor.Process(" hello world ");
// result: "hello world"
Notice that the strategy doesn't track any history. Once you call Process, the result is returned and the strategy has no memory of the operation. This statelessness is what separates it from the command approach in the command vs strategy comparison.
Command Pattern Approach
Now let's solve the same formatting problem with the command pattern. Each command encapsulates the operation, the target text, and enough state to undo it:
public interface ITextCommand
{
void Execute();
void Undo();
}
public sealed class TextDocument
{
public string Content { get; set; } = string.Empty;
}
public sealed class UpperCaseCommand : ITextCommand
{
private readonly TextDocument _document;
private string _previousContent = string.Empty;
public UpperCaseCommand(TextDocument document)
{
_document = document;
}
public void Execute()
{
_previousContent = _document.Content;
_document.Content = _document.Content
.ToUpperInvariant();
}
public void Undo()
{
_document.Content = _previousContent;
}
}
public sealed class TrimCommand : ITextCommand
{
private readonly TextDocument _document;
private string _previousContent = string.Empty;
public TrimCommand(TextDocument document)
{
_document = document;
}
public void Execute()
{
_previousContent = _document.Content;
_document.Content = _document.Content.Trim();
}
public void Undo()
{
_document.Content = _previousContent;
}
}
The invoker manages command execution and maintains a history stack for undo support:
public sealed class TextEditor
{
private readonly Stack<ITextCommand> _history = new();
public void ExecuteCommand(ITextCommand command)
{
command.Execute();
_history.Push(command);
}
public void UndoLastCommand()
{
if (_history.Count > 0)
{
ITextCommand command = _history.Pop();
command.Undo();
}
}
}
Usage shows the key differences -- commands are objects you create, execute through an invoker, and can undo:
var document = new TextDocument
{
Content = " hello world "
};
var editor = new TextEditor();
var trimCmd = new TrimCommand(document);
editor.ExecuteCommand(trimCmd);
// document.Content: "hello world"
var upperCmd = new UpperCaseCommand(document);
editor.ExecuteCommand(upperCmd);
// document.Content: "HELLO WORLD"
editor.UndoLastCommand();
// document.Content: "hello world"
editor.UndoLastCommand();
// document.Content: " hello world "
Each command captures its previous state before executing. The TextEditor pushes executed commands onto a stack, so calling UndoLastCommand walks backwards through the operation history. This is the command vs strategy distinction in action -- the command carries state, supports undo, and decouples when the operation is created from when it runs.
Combining Command and Strategy Patterns
The command and strategy patterns aren't mutually exclusive. Combining them can be very effective when you need both algorithm flexibility and operation tracking. Understanding when to merge these patterns is the next level of mastering command vs strategy in practice.
Consider an image processing pipeline where different filters (strategies) can be applied, but users also need to undo operations. You can use the strategy pattern for the filtering algorithm and wrap each strategy invocation in a command:
public interface IImageFilter
{
byte[] Apply(byte[] imageData);
byte[] Reverse(byte[] imageData);
}
public sealed class ApplyFilterCommand : ITextCommand
{
private readonly TextDocument _document;
private readonly IImageFilter _filter;
private string _previousContent = string.Empty;
public ApplyFilterCommand(
TextDocument document,
IImageFilter filter)
{
_document = document;
_filter = filter;
}
public void Execute()
{
_previousContent = _document.Content;
// In a real scenario, you'd apply the
// filter to image data here
_document.Content =
$"[Filtered: {_filter.GetType().Name}] " +
_document.Content;
}
public void Undo()
{
_document.Content = _previousContent;
}
}
In this combination, the strategy handles the "how" -- which algorithm to use for filtering. The command handles the "what" -- wrapping each filter application as a trackable, undoable operation. This lets users swap filter algorithms freely while maintaining full undo/redo history. The command vs strategy boundary is clean: strategy manages algorithm selection, and command manages execution lifecycle.
This combination works naturally with inversion of control containers, where strategies and commands can both be resolved through dependency injection.
Decision Flowchart: Command vs Strategy
When you're deciding between the command vs strategy pattern, run through these criteria in order:
Do you need undo/redo? If yes, use the command pattern. Strategies don't carry state and can't reverse their effects.
Do you need to queue, schedule, or log operations? If yes, use the command pattern. Commands are self-contained objects that can be serialized, stored, and executed later. Strategies run immediately.
Are you primarily selecting between interchangeable algorithms? If yes, use the strategy pattern. When the decision is about how to do something -- which sorting algorithm, which validation rule, which pricing model -- the strategy pattern is the natural fit.
Do you need to decouple when the operation is defined from when it runs? If yes, use the command pattern. The separation between command creation and execution is a defining characteristic that the strategy pattern doesn't offer. This timing decoupling is a reliable command vs strategy differentiator.
Is the operation stateless -- input in, output out? If yes, the strategy pattern is simpler and more appropriate. Don't add the overhead of command objects when you don't need history, undo, or deferred execution. Statelessness is a strong signal that the command vs strategy decision should lean toward strategy.
Do you need both algorithm flexibility and operation tracking? Combine both patterns. Use strategies for the algorithms and wrap invocations in commands for tracking and undo.
Common Command vs Strategy Mistakes
Developers make several recurring mistakes when choosing between the command vs strategy pattern or implementing either one.
Using commands when a strategy would suffice. If you're just swapping algorithms and don't need undo, history, or deferred execution, the command pattern adds unnecessary complexity. Each command class carries more weight than a strategy because it needs to manage state. Don't pay that cost if you don't need the benefits. This is the most common command vs strategy misapplication.
Building strategies that carry state. If your strategy implementation starts storing data between invocations, you've drifted into command territory. Strategies should be stateless. If you find yourself tracking previous results, maintaining counters, or storing snapshots in a strategy, reconsider whether the command pattern is what you actually need. Recognizing this drift is part of understanding command vs strategy boundaries.
Forgetting to capture state for undo. When using the command pattern, the Execute method must capture enough state to reverse the operation before making changes. A common bug is capturing state after the modification, making Undo restore the wrong state. Always save the snapshot first, then mutate.
Overcomplicating command state management. Some developers try to make every command perfectly reversible, even when the domain doesn't require full undo support. If you only need a history log -- not actual undo -- a simpler command variant without Undo is perfectly valid.
Ignoring the observer pattern for command notifications. When commands execute in a system with UI or logging requirements, you often need to notify other components about command execution. Combining the command pattern with an observer lets you decouple execution from notification cleanly.
Mixing command and strategy responsibilities in a single class. Sometimes developers create a class that both selects an algorithm and tracks execution history. This violates single responsibility and blurs the command vs strategy boundary, making the code harder to test and maintain. Keep algorithm selection (strategy) and operation management (command) in separate classes.
Frequently Asked Questions
What is the main difference between command and strategy pattern?
The main difference between the command vs strategy pattern is their intent. The command pattern encapsulates a request as an object -- it represents an action to be performed, complete with the receiver and parameters needed to execute it. The strategy pattern encapsulates an algorithm and makes it interchangeable -- it represents a way of performing a category of operations. Commands carry state and support undo; strategies are stateless and execute immediately.
Can the command pattern replace the strategy pattern?
Technically, you could use commands everywhere you'd use strategies, but it would be overkill. Commands carry the overhead of state management, undo support, and the invoker infrastructure. If you're simply swapping algorithms -- like choosing between different sorting or validation approaches -- the strategy pattern is simpler and more appropriate. Use the command pattern when you need its specific capabilities: undo/redo, deferred execution, or operation queuing.
When should I use the command pattern in C#?
Use the command pattern when you need to decouple the object that issues a request from the object that executes it. Common scenarios include implementing undo/redo functionality, building macro recording systems, creating task queues or job schedulers, supporting transactional behavior where operations need to be rolled back, and implementing menu systems or toolbar actions in UI applications. Anywhere you need to treat operations as first-class objects that can be stored, queued, or reversed, the command pattern is the right choice.
When should I use the strategy pattern in C#?
Use the strategy pattern when you have multiple algorithms for the same task and want to switch between them at runtime. Common examples include different validation rules, pricing calculations, data formatting algorithms, file export formats, and authentication schemes. The strategy pattern works best when the algorithms are stateless and interchangeable -- you pick one, run it, and you're done. Register your strategies with IServiceCollection to leverage dependency injection for clean strategy resolution.
Can I combine command and strategy patterns together?
Yes -- and it's a powerful combination. Use the strategy pattern to define interchangeable algorithms and the command pattern to wrap each strategy invocation as a trackable, undoable operation. For example, in an image editor, the filter algorithms (blur, sharpen, contrast) are strategies, while each application of a filter is a command that can be undone. The two patterns work at different abstraction levels: strategy handles algorithm selection, command handles execution management.
How does the command vs strategy pattern relate to other design patterns?
Both patterns complement several other behavioral and structural patterns. The command pattern pairs naturally with the observer pattern for event notification when commands execute. It also works well with the decorator pattern to add cross-cutting concerns like logging or validation around command execution. The strategy pattern integrates smoothly with the adapter pattern when you need to adapt third-party algorithms to your strategy interface. Both patterns benefit from inversion of control for managing dependencies.
Is the command pattern the same as CQRS?
No. CQRS (Command Query Responsibility Segregation) is an architectural pattern that separates read and write operations into different models. While it uses the word "command," CQRS commands are not the same as the GoF command pattern. A CQRS command represents an intent to change state in a system, but it doesn't necessarily encapsulate undo logic or carry execution state the way a GoF command does. That said, you can implement the write side of CQRS using the GoF command pattern if you need features like undo, logging, or queuing.
Wrapping Up Command vs Strategy Pattern in C#
The command vs strategy pattern comparison comes down to intent and capabilities. The strategy pattern is the simpler choice when you need interchangeable algorithms without state or undo requirements. The command pattern is the right tool when you need to encapsulate operations as objects -- enabling deferred execution, queuing, logging, and undo/redo support.
Both patterns are behavioral design patterns that encapsulate behavior behind interfaces, which is why they're easy to confuse. The key differentiators are state management (commands carry it, strategies don't), undo capability (commands support it, strategies don't), and execution timing (commands can be deferred, strategies run immediately).
Don't default to one pattern out of habit. Use the decision criteria we covered -- undo requirements, queuing needs, statefulness, and algorithm interchangeability -- to pick the right tool for each situation. And remember that combining both patterns is a valid and often powerful approach when your system needs both algorithm flexibility and operation tracking.

