Summary: in this tutorial, you’ll learn how to use the C# Memento pattern to store the internal state of an object externally without violating the object’s encapsulation.
Introduction to C# Memento pattern
Sometimes, you want to store the internal state of an object externally so that you can restore it later but you don’t want to violate the object’s encapsulation. To do that, you can use the Memento design pattern.
The Memento pattern is a behavioral design pattern that captures and externalizes the internal state of an object so that the object can be restored to the saved state later, without violating encapsulation.
The following diagram illustrates the Memento pattern:
The Memento pattern has the following participants:
Memento
is responsible for storing the internal state of theOriginator
object. It may store as much or as little of the internal state depending on theOriginator
object’s needs.Originator
is an object whose state needs to be saved and restored. TheOriginator
object creates aMemento
object that captures a snapshot of its current internal state. TheOriginator
uses theMemento
to restore its internal state.Caretaker
is in charge of managing the Mementos. TheCaretaker
object requests aMemento
object from theOriginator
, stores it, and later uses it to restore theOriginator
.
In practice, you’ll find the Memento pattern helpful in the following cases:
- Undo/rollback: You can use the Memento pattern to implement undo/rollback functionality, where you can restore an object’s state to a previous state. This is useful in applications where users make changes that they want to undo, like in a text editor.
- Versioning: You can also use the Memento pattern to implement versioning, where you can save an object’s state at different points in time. This is useful in applications where it is important to keep track of changes made to an object over time, such as in a document management system.
C# Memento pattern example
The following program illustrates how to implement the Memento pattern in C#:
namespace MemetoPattern;
public class Memento
{
public string? State
{
get;
set;
}
public Memento(string? state)
{
State = state;
}
}
public class Originator
{
public string? State
{
get; set;
}
public void RestoreState(Memento memento)
{
State = memento.State;
}
public Memento SaveState()
{
return new Memento(State);
}
}
public class Caretaker
{
public static void Main(string[] args)
{
var originator = new Originator
{
State = "State 1"
};
// Save the state
var memento = originator.SaveState();
// Change the state
originator.State = "State 2";
// Restore the state from the memento object
originator.RestoreState(memento);
// Display the state
Console.WriteLine(originator.State); // State 1
}
}
Code language: C# (cs)
Output:
State 1
Code language: C# (cs)
How it works.
First, define the Memento class that captures the state of an object. The state is a simple nullable string:
public class Memento
{
public string? State
{
get;
set;
}
public Memento(string? state)
{
State = state;
}
}
Code language: C# (cs)
Second, define the Originator object whose internal state is saved by a Memento object:
public class Originator
{
public string? State
{
get; set;
}
public void RestoreState(Memento memento)
{
State = memento.State;
}
public Memento SaveState()
{
return new Memento(State);
}
}
Code language: C# (cs)
The Originator class has a State property as a string and the SaveState()
and RestoreState()
methods.
The SaveState()
method returns a new Memento object with the current State. And the RestoreState
restores the State from a Memento object by assigning the State property to the State of the Memento object.
Third, define the Caretaker class that creates an Originator object with the State 1, saves its state into a Memento object, changes the state of the Originator object to “State 2”, and restores the state of the Originator object to “State 1” using the Memento object:
public class Caretaker
{
public static void Main(string[] args)
{
var originator = new Originator
{
State = "State 1"
};
// Save the state
var memento = originator.SaveState();
// Change the state
originator.State = "State 2";
// Restore the state from the memento object
originator.RestoreState(memento);
// Display the state
Console.WriteLine(originator.State); // State 1
}
}
Code language: C# (cs)
It’s possible to save multiple states of the originator object and restore it to a specific state later. For example:
public class Caretaker
{
public static void Main(string[] args)
{
//
var savedStates = new List<Memento>();
var originator = new Originator
{
State = "State 1"
};
// Save the State 1
savedStates.Add(originator.SaveState());
// Change the state
originator.State = "State 2";
// Save the State 2
savedStates.Add(originator.SaveState());
originator.State = "State 3";
// Restore the intial state
originator.RestoreState(savedStates[0]);
Console.WriteLine(originator.State); // State 1
}
}
Code language: C# (cs)
Output:
State 1
Code language: C# (cs)
Summary
- Use the Memento pattern to store the internal state of an object externally without violating encapsulation.