Command Pattern in C#: Definition, Examples, Pros, Cons, and Use Cases

Command Pattern in C#: Definition, Examples, Pros, Cons, and Use Cases

The Command pattern in C# is a behavioral design pattern that encapsulates a request or operation as an object, allowing parameterization, queuing, logging, and undoable operations.

The Command pattern converts requests into standalone command objects that contain all information needed to execute an action. It separates the object that invokes an operation from the object that performs it. A command object usually contains a reference to a receiver object and a method that executes the request. This pattern supports flexible operation management such as undo/redo, task scheduling, transaction handling, and command queues. In C#, the Command pattern is widely used in GUI applications, background job systems, menu systems, workflow engines, and CQRS architectures.

Why We Use Command Pattern in C#?

We use the Command pattern in C# to:

• Encapsulate operations into objects
• Decouple senders from receivers
• Support undo/redo functionality
• Enable command queues
• Allow delayed execution
• Simplify logging and auditing
• Improve extensibility
• Support transactional operations

When Should We Use Command Pattern in C#?

The Command pattern should be used when:

• Operations need to be parameterized
• Undo/redo functionality is required
• Commands should be queued or scheduled
• Request senders should not know execution details
• Multiple operations should share a common interface
• Actions need logging or auditing
• Macro or batch operations are required

Common Programming Problems Solved by Command Pattern

• Undo/redo systems
• Task queues
• Background job processing
• GUI button actions
• Macro recording systems
• Transaction processing
• Event-driven systems
• CQRS command handling
• Workflow execution systems

Structure of Command Pattern in C#

Typical participants:

• Command Interface
• Concrete Command
• Receiver
• Invoker
• Client

Basic workflow:

• Client creates command object
• Command stores receiver reference
• Invoker triggers command execution
• Receiver performs actual operation

Command Pattern Examples in C#

Example 1: Remote Control System

A remote control should trigger different device actions without knowing implementation details.

using System;

public interface ICommand
{
    void Execute();
}

public class Light
{
    public void TurnOn()
    {
        Console.WriteLine("Light is ON");
    }
}

public class LightOnCommand : ICommand
{
    private Light light;

    public LightOnCommand(Light light)
    {
        this.light = light;
    }

    public void Execute()
    {
        light.TurnOn();
    }
}

public class RemoteControl
{
    private ICommand command;

    public void SetCommand(ICommand command)
    {
        this.command = command;
    }

    public void PressButton()
    {
        command.Execute();
    }
}

public class Program
{
    public static void Main()
    {
        Light light = new Light();

        ICommand lightCommand = new LightOnCommand(light);

        RemoteControl remote = new RemoteControl();

        remote.SetCommand(lightCommand);

        remote.PressButton();
    }
}

Example 2: Undo/Redo Text Editor

A text editor should support undo functionality.

using System;
using System.Collections.Generic;

public interface ICommand
{
    void Execute();
    void Undo();
}

public class TextEditor
{
    public string Text { get; private set; } = "";

    public void Write(string value)
    {
        Text += value;
    }

    public void Erase(string value)
    {
        if (Text.EndsWith(value))
        {
            Text = Text.Substring(0, Text.Length - value.Length);
        }
    }
}

public class WriteCommand : ICommand
{
    private TextEditor editor;
    private string text;

    public WriteCommand(TextEditor editor, string text)
    {
        this.editor = editor;
        this.text = text;
    }

    public void Execute()
    {
        editor.Write(text);
    }

    public void Undo()
    {
        editor.Erase(text);
    }
}

public class Program
{
    public static void Main()
    {
        TextEditor editor = new TextEditor();

        ICommand command = new WriteCommand(editor, "Hello");

        command.Execute();

        Console.WriteLine(editor.Text);

        command.Undo();

        Console.WriteLine(editor.Text);
    }
}

Example 3: Job Queue System

Tasks should be queued and executed later.

using System;
using System.Collections.Generic;

public interface ICommand
{
    void Execute();
}

public class EmailService
{
    public void SendEmail()
    {
        Console.WriteLine("Email sent");
    }
}

public class EmailCommand : ICommand
{
    private EmailService service;

    public EmailCommand(EmailService service)
    {
        this.service = service;
    }

    public void Execute()
    {
        service.SendEmail();
    }
}

public class JobQueue
{
    private Queue<ICommand> commands = new Queue<ICommand>();

    public void AddJob(ICommand command)
    {
        commands.Enqueue(command);
    }

    public void ProcessJobs()
    {
        while (commands.Count > 0)
        {
            ICommand command = commands.Dequeue();
            command.Execute();
        }
    }
}

public class Program
{
    public static void Main()
    {
        EmailService service = new EmailService();

        JobQueue queue = new JobQueue();

        queue.AddJob(new EmailCommand(service));

        queue.ProcessJobs();
    }
}

Most Known Real-World Use Cases in C#

• Undo/redo systems
• GUI menu and button actions
• Background task queues
• Workflow engines
• CQRS command handling
• Macro recording systems
• Transaction processing
• Job schedulers
• Event sourcing systems

Advantages of Command Pattern in C#

• Decouples sender and receiver
• Supports undo/redo operations
• Enables command queuing
• Simplifies logging and auditing
• Supports delayed execution
• Improves extensibility
• Encourages Single Responsibility Principle
• Makes macro operations easier

Disadvantages (Weak Points) of Command Pattern in C#

• Increases number of classes
• Adds architectural complexity
• Simple operations may become over-engineered
• Undo logic can become difficult
• Command history may consume memory
• Debugging command chains can be harder
• May introduce additional abstraction overhead

Comparison with Similar Patterns

Pattern Main Purpose Encapsulation Type Execution Style Typical Usage Main Difference from Command
Command Encapsulate requests as objects Operations Triggered by invoker Undo systems, queues Focuses on request encapsulation and execution
Strategy Switch algorithms dynamically Algorithms Direct execution Sorting, pricing Encapsulates algorithms instead of executable requests
Memento Save and restore state Object state Snapshot restoration Undo/redo systems Stores state snapshots instead of executable operations
Chain of Responsibility Pass requests through handlers Request processing Sequential chain Middleware pipelines Passes requests through handlers instead of encapsulating actions
Mediator Centralize communication Object interactions Through mediator UI coordination Coordinates communication instead of encapsulating operations

Simplified UML-Style Structure

Component Responsibility
Command Interface Defines execute operation
ConcreteCommand Implements request execution
Receiver Performs actual business operation
Invoker Triggers command execution
Client Creates and configures commands

Creational Patterns in C#

13. Chain of Responsibility
14. Iterator
15. Momento
16. State
17. Template Method
18. Command
19. Mediator
20. Observer
21. Strategy
22. Visitor