Design Patterns & Practices  

Understanding the Memento Design Pattern in C# with a Practical Example

Introduction

In software development, we often need to save an object's state and restore it later—for example, to build an "Undo" button. However, doing this manually often forces you to expose private data, which ruins your code's encapsulation. The Memento Design Pattern solves this by letting you capture and restore a private state without exposing the object's internal secrets.

In this article, we will explore the Memento Pattern to understand how it captures an object's state, when to use it, how its structure works, and how to implement a real-world Undo functionality in C#.

What Is the Memento Pattern?

The Memento Pattern is a behavioral design pattern that lets you take a "snapshot" of an object’s current state so you can restore it later.

The key benefit is that it does this without exposing the object's internal details. In simple terms, it allows you to hit "Save" on an object's state today and "Undo" back to that exact moment tomorrow, all while keeping the object's private data hidden from the rest of your code.

A Real-Life Example:

Very Simple : The Text Editor

Think about how a standard Text Editor works:

  • The Action: You type a new paragraph.

  • The Mistake: You accidentally delete a crucial sentence.

  • The Fix: You press Undo, and the previous version instantly reappears.

To make this happen, the editor must "remember" every previous state. However, it needs to do this without letting the "Undo" system mess with the internal logic of how the text is actually stored. The Memento Pattern acts as that hidden memory, allowing you to jump back in time whenever a mistake happens.

Structure of the Memento Pattern

The Memento Pattern relies on three specific roles to manage snapshots efficiently:

  • Originator: The main object whose state you want to save (e.g., the Text Editor). It is responsible for creating snapshots and restoring itself from them.

  • Memento: A small, private object that acts as the "snapshot." It stores the state data but prevents other classes from messing with it.

  • Caretaker: The manager that keeps track of the snapshots (e.g., an Undo history list). It knows when to save or restore but doesn't know what is inside the Memento

Practical Example: Simple Text Editor with Undo

Here is the step-by-step C# implementation of the Memento Pattern, broken down into its three core roles:

Step 1: The Snapshot (Memento)

First, we create a simple class to hold the data. We make the state read-only so that once a snapshot is taken, it cannot be accidentally changed.

public class TextMemento
{
    public string Content { get; }
    public TextMemento(string content) => Content = content;
}

Step 2: The Main Object (Originator)

The TextEditor is the "brain" of the operation. It is responsible for creating its own snapshots and restoring its state from a previously saved Memento.

public class TextEditor
{
    public string Content { get; set; }

    // Creates a new snapshot of the current text
    public TextMemento Save() => new TextMemento(Content);

    // Restores the text from a previous snapshot
    public void Restore(TextMemento memento) => Content = memento.Content;
}

Step 3: The History Manager (Caretaker)

The History class keeps track of all our snapshots. We use a Stack here because "Undo" always restores the most recent change first (Last-In, First-Out).

public class History
{
    private readonly Stack<TextMemento> _snapshots = new();

    public void Push(TextMemento memento) => _snapshots.Push(memento);
    public TextMemento Pop() => _snapshots.Pop();
}

Step 4: Putting It Together

Now we can see the "Undo" functionality in action. Notice how the Program doesn't need to know how the text is stored; it just tells the history to save or restore.

var editor = new TextEditor();
var history = new History();

// Type something and save it
editor.Content = "Version 1";
history.Push(editor.Save());

// Type something else and save it
editor.Content = "Version 2";
history.Push(editor.Save());

editor.Content = "Version 3 (Current)";
Console.WriteLine($"Current: {editor.Content}");

// Perform an Undo
editor.Restore(history.Pop());
Console.WriteLine($"After Undo: {editor.Content}");

Output

Current: Version 3
After Undo: Version 2

By using this pattern, you’ve built a robust Undo system that respects encapsulation. The History class manages the list of changes, while theTextEditor manages its own internal data.

What Just Happened?

Here is a breakdown of how the pattern functioned in the example:

  • State Capture: The editor successfully took a snapshot of its data before any new changes were applied.

  • Safe Storage: The caretaker (history) kept these previous versions organized without needing to understand the text inside them.

  • Seamless Restoration: When the "Undo" command was triggered, the editor instantly reverted to the exact state it was in previously.

  • Protected Logic: The internal data structure remained private, ensuring that the history manager could never accidentally corrupt the editor's content

When Should You Use the Memento Pattern?

You should reach for the Memento Pattern in these specific scenarios:

  • Undo/Redo Features: When your application needs a "time travel" mechanism for user actions.

  • Temporary State Storage: When you need to cache an object's current state before performing a risky or temporary operation.

  • Version History: When you want to allow users to browse and restore previous versions of their work.

  • Strict Encapsulation: When you must save state without letting external classes "peek" at or modify the object's private data.

Why Use the Memento Pattern?

  • Preserves Encapsulation: It saves the object's data without exposing its private fields to the rest of the application.

  • Simplifies Undo/Redo: You don't have to write complex logic to "reverse" an action; you just swap the state back.

  • Organised State Management: It moves the burden of remembering the past away from the main business logic and into a dedicated history manager.

  • Separation of Responsibilities: The object focuses on its work, while the caretaker focuses purely on tracking changes.

Real-World Usage in .NET

You will find the Memento Pattern powering features in many professional applications:

  • Text Editors: Managing the character-by-character history for Ctrl+Z functionality.

  • Drawing & Design Apps: Storing layers and brush strokes so you can revert mistakes in tools like Photoshop or Canva.

  • Game Development: Implementing "Save Points" or "Rewind" mechanics by capturing the player's position and stats.

  • Transaction Rollbacks: Temporarily caching data before a complex operation so you can revert if a database error occurs.

Conclusion

In this article, we have seen how the Memento Design Pattern allows you to capture and restore an object’s state safely and cleanly. We explored how it provides a structured way to implement undo/redo functionality while ensuring that your object’s internal data remains private and protected.

Hope this helps!