Skip to content

Repository files navigation

CommandManagement

A simple implementation of the Undo/Redo Command pattern

Usage example

Below is a minimal example that demonstrates creating a simple Command, executing it with CommandManager, then undoing and redoing the command.

using System;
using Skc.BestPractices.CommandManager;

// A tiny mutable object we operate on
class Counter
{
    public int Value;
}

// A simple command that increments the counter and can undo the increment
class IncrementCommand : Command
{
    private readonly Counter _counter;
    private readonly int _amount;

    public IncrementCommand(Counter counter, int amount)
    {
        _counter = counter;
        _amount = amount;
        Description = $"Increment by {_amount}";
    }

    protected internal override object Execute()
    {
        _counter.Value += _amount;
        return null;
    }

    protected internal override object Undo()
    {
        _counter.Value -= _amount;
        return null;
    }
}

class Program
{
    static void Main()
    {
        var manager = new CommandManager();
        var counter = new Counter();

        var cmd = new IncrementCommand(counter, 5);

        manager.Execute(cmd);
        Console.WriteLine(counter.Value); // 5

        manager.Undo();
        Console.WriteLine(counter.Value); // 0

        manager.Redo();
        Console.WriteLine(counter.Value); // 5
    }
}

Thread-safety & usage patterns

  • Thread-safety: CommandManager is not thread-safe (it uses no synchronization). If you call Execute/Undo/Redo from multiple threads, guard access with your own synchronization (e.g., a lock) or ensure all calls are marshaled to a single thread (UI thread or a dedicated worker).
  • Events: subscribe to CommandManager events to update UI or react to changes: CommandHistoryChanged, CommandFutureChanged, Executing, Executed, Discarded. Event handlers are invoked synchronously on the thread that performed the operation — if you update UI from an event handler, marshal to the UI thread.
  • Long-running work: keep Execute/Undo implementations fast. If a command needs to perform I/O or long-running work, run that work on a background thread and record only the minimal state needed for undo, or consider splitting the command into a quick state-change command and a separate background task.
  • Groups & markers: use BeginGroup/EndGroup for multi-step actions (macros). Use SetMarker/IsAtMarker to track unsaved changes.

Build & test

# restore & build
dotnet build Skc.BestPractices.CommandManagement.sln

# run tests (tests target net48; run on Windows or with appropriate runtimes)
dotnet test Skc.BestPractices.CommandManager.Tests/Skc.BestPractices.CommandManager.Tests.csproj

About

A simple implementation of the Undo/Redo Command pattern

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages