Memento Pattern in C#: Save and Restore Object State
This article explores the Memento design pattern in C#, providing an overview of how it captures and externalizes an object's internal state without violating encapsulation. You will learn the roles of the pattern's three primary components—the Originator, the Memento, and the Caretaker—and walk through a practical C# code implementation that demonstrates how to create snapshots and implement undo functionality cleanly.
Understanding the Memento Pattern
The Memento pattern is a behavioral design pattern used to save and restore the previous state of an object. Its primary advantage is that it enables undo mechanisms, checkpoints, or transaction rollbacks without exposing the private fields or implementation details of the target object.
The pattern consists of three core components:
- Originator: The primary business object whose state needs saving. It creates mementos containing snapshots of its current state and uses them to revert to previous states.
- Memento: A lightweight, immutable object that stores the Originator's state. It should restrict access to its data, allowing only the Originator to read or write its contents.
- Caretaker: The object responsible for keeping track of mementos (such as an undo stack). It stores mementos but never inspects or alters their contents.
Implementing the Memento Pattern in C
The following implementation models a simple text editor with undo functionality.
1. The Memento
The memento holds the state data and is made immutable to prevent modification while stored in the history.
public class EditorMemento
{
public string Content { get; }
public EditorMemento(string content)
{
Content = content;
}
}2. The Originator
The TextEditor creates mementos of its current state and
uses existing mementos to restore its state.
public class TextEditor
{
public string Content { get; set; } = string.Empty;
public EditorMemento Save()
{
return new EditorMemento(Content);
}
public void Restore(EditorMemento memento)
{
if (memento != null)
{
Content = memento.Content;
}
}
}3. The Caretaker
The HistoryManager maintains a stack of mementos,
enabling sequential undo operations.
using System.Collections.Generic;
public class HistoryManager
{
private readonly Stack<EditorMemento> _history = new();
public void Push(EditorMemento memento)
{
_history.Push(memento);
}
public EditorMemento? Pop()
{
return _history.Count > 0 ? _history.Pop() : null;
}
}4. Putting It Together
using System;
class Program
{
static void Main()
{
var editor = new TextEditor();
var history = new HistoryManager();
editor.Content = "Initial draft.";
history.Push(editor.Save());
editor.Content = "Second revision.";
history.Push(editor.Save());
editor.Content = "Unwanted changes.";
Console.WriteLine($"Current: {editor.Content}");
// Undo to "Second revision."
var previousState = history.Pop();
if (previousState != null)
{
editor.Restore(previousState);
}
Console.WriteLine($"Restored: {editor.Content}");
// Undo to "Initial draft."
previousState = history.Pop();
if (previousState != null)
{
editor.Restore(previousState);
}
Console.WriteLine($"Restored: {editor.Content}");
}
}When to Use the Memento Pattern
Use the Memento pattern when:
- You need to build multi-level undo/redo operations or rollback mechanisms.
- Direct access to an object's fields or properties would violate encapsulation boundaries.
- Saving snapshots of complex application state is necessary for transaction management or crash recovery.
Be mindful of memory consumption when frequently creating state snapshots of large objects. In high-frequency scenarios, consider saving state deltas instead of full object copies.