[Home](../) › [Blog](../blog) › The Memento Design Pattern in C#, Practically With Examples [2024]

# The Memento Design Pattern in C#, Practically With Examples [2024]

> Discover how to implement the Memento design pattern in C# to create user-friendly applications with undo and redo functionalities, enhancing user experience and boosting your app’s robustness.

Published: 2024-07-25 · Author: Metalama Team · Canonical: https://postsharp.net/blog/memento

The Memento pattern is one of the foundational design patterns. It helps build UI features like undo/redo or UI transactions. In this article, we will see how to implement the Memento pattern in C#. We use a small WPF-based Fish Tank Manager as a sample application.

## What is the Memento design pattern?

The **Memento design pattern** is a software design pattern that provides the ability to restore an object to its previous state without revealing its internal structure. The aim is to provide standardization in scenarios where the object's state needs to be saved and restored, such as the GUI undo/redo mechanism we presented earlier, but also for, for example, in-memory transactions.

The Memento pattern consists of three key components:
- **Originator**: The object whose state needs to be saved and restored.
- **Memento**: An object that contains a snapshot of the Originator's state.
- **Caretaker**: The object that keeps track of multiple mementos, often implementing mechanisms to store and retrieve them.

While using the Memento pattern, two processes occur:
- **Capture**: When a state snapshot is needed, the Originator creates a Memento object and passes it to the Caretaker for safekeeping.
- **Restore**: To restore the state, the Caretaker provides the stored Memento back to the Originator, which rewrites its state.

## When to use the Memento pattern?

Here are some situations when you might use the Memento pattern:

1. **Undo/Redo Functionality**: When you need to implement undo and redo mechanisms, the Memento pattern is ideal. It allows you to save the state of an object at a certain point and revert back to it when needed. For example, in text editors, graphic editors, or games.

2. **State History or Snapshot Tracking**: If an application requires keeping a history of different states of an object, the Memento pattern can be useful. This is common in version control systems or in any system where you need to track changes over time.

3. **Transaction Management**: In applications where transactions are used (like database operations), the Memento pattern can help in saving the state before the transaction starts. If the transaction fails, the state can be restored to its original state.

When introduced in an application, the Memento pattern must be exhaustively implemented in the whole object model for the user experience to be consistent. This means significant implementation and maintenance costs.

## Example: Fish in a Fish Tank

Imagine you are implementing an application that allows for tracking fish in your home fish tank.

The application consists of a list of fish, allowing you to add a fish, remove a fish, and edit a fish.

Let's see how it looks, with its beatiful Undo button:

![Initial App](https://postsharp.net/assets/images/2024/2024-07-memento/app_final-835907f9b2.png)

The app, which is implemented in WPF, initially has no functionality for undoing changes. We will add this as the article progresses. In particular, the `Fish` class, which represents a single fish, looks like this:

```csharp
public sealed class Fish : ObservableRecipient
{
    private string? _name;
    private string? _species;
    private DateTime _dateAdded;

    public string? Name
    {
        get => this._name;
        set => this.SetProperty( ref this._name, value, true );
    }

    public string? Species
    {
        get => this._species;
        set => this.SetProperty( ref this._species, value, true );
    }

    public DateTime DateAdded
    {
        get => this._dateAdded;
        set => this.SetProperty( ref this._dateAdded, value, true );
    }
}
```

As you can see, the class is initially pretty simple and contains only the data fields.

Let's also look at the data fields of the `MainViewModel` class, which we will change later:

```csharp
private readonly IFishGenerator _fishGenerator;
private bool _isEditing;
private Fish? _currentFish;

public ObservableCollection<Fish> Fishes { get; } = new();
```

The full source code of examples in this article is available on [GitHub](https://github.com/postsharp/TimelessDotNetEngineer/tree/main/src/patterns/memento).

## How to implement the Memento pattern in C#?

The Memento pattern is implemented by defining interfaces for the Memento, Originator, and Caretaker:
- The `IMementoable` interface is implemented by _originator_ objects that hold state needing to be captured and restored. It allows `IMementoCaretaker` to retrieve memento objects and restore the state of the object.

    ```csharp
public interface IMementoable
{
    IMemento SaveToMemento();

    void RestoreMemento( IMemento memento );
}
```

- The `IMemento` interface is minimalistic: it just exposes an `Originator` property used by the `IMementoCaretaker` during the Undo process.

    ```csharp
public interface IMemento
{
    IMementoable Originator { get; }
}
```

- The `IMementoCaretaker` is a service that manages stored `IMemento` instances and uses them to restore states of `IMementoable` objects.

    ```csharp
public interface IMementoCaretaker
{
    bool CanUndo { get; }

    void CaptureSnapshot( IMementoable mementoable );

    void Undo();
}
```

In C#, it's best to define the implementation of the `IMemento` interface as a _private_ nested class. This stays true to the primary goal of the Memento pattern to respect encapsulation and allow the memento to access the private state of the originator.

A straightforward approach involves manually implementing the `IMementoable` and `IMemento` interfaces. In our example, this involves declaring a `Memento` nested type in both `MainViewModel` and `Fish`.

### Step 1: Implement the Caretaker class

First, we implement the `Caretaker` class that implements the `IMementoCaretaker` interface. Our implementation is very simple: just a stack of memento objects.

```csharp
public sealed class MementoCaretaker : IMementoCaretaker
{
    private readonly Stack<IMemento> _mementos = new();

    public void CaptureSnapshot( IMementoable mementoable )
    {
        this._mementos.Push( mementoable.SaveToMemento() );
    }

    public void Undo()
    {
        if ( this._mementos.Count > 0 )
        {
            var memento = this._mementos.Pop();
            memento.Originator.RestoreMemento( memento );
        }
    }

    public bool CanUndo => this._mementos.Count > 0;
}
```

### Step 2: Register the Caretaker service

Conceptually, the Caretaker is often a Singleton. In a modern .NET app, it's a good practice to use `appBuilder.Services.AddSingleton` to register it.

```csharp
services.AddSingleton<IMementoCaretaker, MementoCaretaker>();
```

### Step 3: Implement the IMementoable interface

Here is the most boring and repetitive part. We must implement the `IMementoable` interface for all classes that hold undoable state. In each of these classes, we create the following artifacts:

* A private nested `Memento` with the following members:
   * For each mutable field or property of the originator class, a public field of the same name and type.
   * An implementation of the `IMemento` interface, specifically the `Originator` property.
   * A constructor that stores the fields and properties of the originator class into the properties of the `Memento` class, as well as the `Originator` property.
* An implementation of the `IMementoable` interface including:
   * The `SaveToMemento` method, returning an instance of the `Memento` nested class.
   * The `RestoreMemento` method, copying the memento properties back into the originator's fields and properties.

Let's implement this pattern into the `Fish` class:

```csharp
public sealed class Fish : ObservableRecipient, IMementoable
{
    private string? _name;
    private string? _species;
    private DateTime _dateAdded;

    public string? Name
    {
        get => this._name;
        set => this.SetProperty( ref this._name, value, true );
    }

    public string? Species
    {
        get => this._species;
        set => this.SetProperty( ref this._species, value, true );
    }

    public DateTime DateAdded
    {
        get => this._dateAdded;
        set => this.SetProperty( ref this._dateAdded, value, true );
    }

    public void RestoreMemento( IMemento memento )
    {
        if ( memento is not Memento s )
        {
            throw new InvalidOperationException( "Invalid snapshot." );
        }

        this.Name = s.Name;
        this.Species = s.Species;
        this.DateAdded = s.DateAdded;
    }

    public IMemento SaveToMemento()
    {
        return new Memento( this, this.Name, this.Species, this.DateAdded );
    }

    private sealed record Memento(
        Fish Originator,
        string? Name,
        string? Species,
        DateTime DateAdded ) : IMemento
    {
        IMementoable IMemento.Originator => this.Originator;
    }
}
```

### Step 4: Coping with collection types

When implementing the Memento pattern, ensure that _all_ mutable classes implement it. For collections, you have two options: either implement `IMementoable` collections or use immutable collections. In this example, we use the second approach. In `MainViewModel`, we trade our `ObservableCollection<Fish>` for an `ImmutableList<Fish>`. Using `ImmutableList` instead of, say, an `ImmutableArray`, is a good choice because `ImmutableList` tries to reuse as much memory as possible between different edits of the list, while `ImmutableArray` would always create a new copy.

Let's see how the data fields of `MainViewModel` have changed:

```csharp
private readonly IMementoCaretaker? _caretaker;
private readonly IFishGenerator _fishGenerator;
private bool _isEditing;
private Fish? _currentFish;
private ImmutableList<Fish> _fishes = ImmutableList<Fish>.Empty;
```

Also, see the `Memento` class for `MainViewModel`:

```csharp
private class Memento : IMemento
{
    public IMementoable Originator { get; }

    public bool IsEditing { get; }

    public Fish? CurrentItem { get; }

    public ImmutableList<Fish> Items { get; }

    public Memento(
        MainViewModel snapshotable,
        bool isEditing,
        Fish? currentItem,
        ImmutableList<Fish> items )
    {
        this.Originator = snapshotable;
        this.IsEditing = isEditing;
        this.CurrentItem = currentItem;
        this.Items = items;
    }
}
```

### Step 5: Calling CaptureMemento and Undo

So far, we have only implemented the `IMementoable` pattern, and we should start using it to add functionality to the application.

An inconvenience of the Memento pattern is that the `IMementoCaretaker.CaptureMemento` method must be called _manually_. If you're implementing an undo/redo feature, you would typically call it _before_ any operation.

```csharp
private void ExecuteNew()
{
    this._caretaker?.CaptureSnapshot( this );

    this.Fishes = this.Fishes.Add(
        new Fish()
        {
            Name = this._fishGenerator.GetNewName(),
            Species = this._fishGenerator.GetNewSpecies(),
            DateAdded = DateTime.Now
        } );
}
```

If you're still using modal dialogs with a transactional user interface based on the _Ok_ and _Cancel_ buttons, you can capture a memento when the dialog opens, and call _Undo_ if the user hits _Cancel_.

For instance, here is the code that opens the UI in edit mode (`ExecuteEdit`) and handles the OK (`ExecuteSave`) and Cancel (`ExecuteCancel`) buttons.

```csharp
private void ExecuteEdit()
{
    this.IsEditing = true;

    this._caretaker?.CaptureSnapshot( this._currentFish! );
}

private void ExecuteSave()
{
    this.IsEditing = false;
}

private void ExecuteCancel()
{
    this.IsEditing = false;
    this._caretaker?.Undo();
}
```

As you can see, the Memento pattern can be used to implement UI transactions.

## How to implement Memento without boilerplate?

As you can judge from Step 3 above, implementing Memento types follows a very repetitive pattern. Each Memento type must declare fields that correspond to the target class, create a similar constructor, implement the same interface, etc. There is virtually no reason to implement this pattern by hand. Adopting a code generation technology saves both implementation time and maintenance costs. And a lot of gray hairs, because a code generator, unlike a human, won't forget to add a property to the Memento when one is added to the Originator!

The .NET community offers several code generation technologies which can be grouped into two categories: run-time and compile-time generation. Run-time generation should be avoided because it adds a startup performance overhead and is difficult to debug. Within the second category, you have two options: source generators like Roslyn source generators or [Metalama](https://www.postsharp.net/metalama), and, historically, MSIL generators like [Fody](https://github.com/Fody/Fody). Fody and Roslyn source generators are very low-level and tend to be overly complex. [Metalama](https://www.postsharp.net/metalama), on the other hand, offers a simpler API and development experience. Metalama is free to use. It is based on Roslyn and actually uses Roslyn source generators under the hood.

With Metalama, you can reduce the pattern to a single attribute, `MementoAttribute`, called an [aspect](https://www.postsharp.net/solutions/aspect-oriented-programming). This aspect generates the Memento code on-the-fly during compilation, keeping your source code clean and succinct. The aspect automatically performs all the steps we mentioned above. Going through all implementation steps of this aspect is beyond the scope of this article. We refer you to [The Memento Pattern](https://doc.postsharp.net/metalama/examples/memento) in Metalama documentation for details.

Metalama also comes with other open-source [aspects](https://www.postsharp.net/solutions/aspect-oriented-programming), such as [Observable](https://doc.postsharp.net/metalama/patterns/observability/observability), which automatically implements the `INotifyPropertyChanged` interface, and [WPF Command](https://doc.postsharp.net/metalama/patterns/wpf/command).

When we use these aspects in our project, the `Fish` class gets reduced to its essence:

```csharp
[Memento]
[Observable]
public sealed partial class Fish
{
    public string? Name { get; set; }

    public string? Species { get; set; }

    public DateTime DateAdded { get; set; }
}
```

The code that handles `IMementoable` and `INotifyPropertyChanged` is no longer necessary in your _source_ code because it is generated during compilation. So, we've saved work and, at the same time, improved the reliability and maintainability of the code. If we have 50 classes implementing the Memento pattern, we still have just one `MementoAttribute` to maintain.

## How to automatically capture snapshots upon changes?

You're certainly aware that there are two approaches to user interfaces for data entry. One is based on explicit _Ok_ and _Cancel_ buttons. The second auto-saves any change. In this case, a good undo/redo feature is almost mandatory.

If you are building an auto-save user experience, it's a good idea to trigger the call to `CaptureMemento` whenever a property changes.

If your object model already implements `INotifyPropertyChanged`, it's reasonably easy to subscribe to the `PropertyChanged` event and call `CaptureMemento` upon any change. For instance, we added this logic to `FishControl.xaml.cs`. If you have several windows or controls, you can move this logic to a base class, or even to an aspect if you're using Metalama.

```csharp
protected override void OnPropertyChanged( DependencyPropertyChangedEventArgs e )
{
    base.OnPropertyChanged( e );

    if ( e.Property.Name == nameof(this.DataContext) )
    {
        if ( e.OldValue is IMementoable and INotifyPropertyChanged newObservable )
        {
            newObservable.PropertyChanged -= this.OnMementoablePropertyChanged;
        }

        if ( e.NewValue is IMementoable newMementoable and INotifyPropertyChanged newObervable )
        {
            newObervable.PropertyChanged += this.OnMementoablePropertyChanged;

            // Capture _before_ any change.
            this._caretaker?.CaptureMemento( newMementoable );
        }
    }
}

private void OnMementoablePropertyChanged( object? sender, PropertyChangedEventArgs e )
{
    var mementoable = (IMementoable?) this.DataContext;

    if ( mementoable != null )
    {
        this._caretaker?.CaptureMemento( mementoable );
    }
}
```

In WPF, this approach will save a memento whenever the user focus switches from one control to another.

If your user is expected to type long texts, you might want to save the state more often. In this case, you can cause the binding to be updated on every keystroke. You can do this by hooking the `KeyUp` event.

```xml
<TextBox Grid.Row="0" Grid.Column="1" Margin="2" Text="{Binding Name}" KeyUp="OnTextBoxUpdated" />
```

```csharp
private void OnTextBoxUpdated( object sender, KeyEventArgs e )
{
    if ( sender is TextBox textBox )
    {
        var binding = BindingOperations.GetBindingExpression( textBox, TextBox.TextProperty );

        if ( binding != null )
        {
            binding.UpdateSource();
        }
    }
}
```

However, this strategy creates a new snapshot _for every keystroke_, which is highly inconvenient both for the user and for memory usage.

To mitigate this problem, the Caretaker can ignore a new memento if the last memento is recent and of identical originator.

1. Modify the `IMemento` interface to this:

   ```csharp
public interface IMemento
{
    IMementoable Originator { get; }

    DateTime MementoTime { get; }
}
```

2. Modify the implementation pattern to set the value of the `MementoTime` property to `DateTime.Now`.
3. Change `Caretaker.CaptureMemento` to ignore requests that are too close to each other.

    ```csharp
public void CaptureMemento( IMementoable mementoable )
{
    if ( this._mementos.Count > 0 )
    {
        var lastMemento = this._mementos.Peek();

        if ( lastMemento.MementoTime > DateTime.Now.AddSeconds( -5 )
             && lastMemento.Originator == mementoable )
        {
            // Ignore.
            return;
        }
    }

    this._mementos.Push( mementoable.SaveToMemento() );
    this.OnPropertyChanged( nameof(this.CanUndo) );
}
```

Now, you will have a savepoint every 5th second at most for each object.

## How to implement IEditableObject using the Memento pattern

The `IEditableObject` is the system interface for editable objects that support Ok-Cancel semantics. It is used by controls such as `DataGrid`.

```cs
public interface IEditableObject
{
    void BeginEdit();
    void EndEdit();
    void CancelEdit();
}
```

It's easy to implement `IEditableObject` if your class already implements `IMementoable`. Here is an example.

```csharp
public abstract partial class EditableObject : IEditableObject, IMementoable
{
    private IMemento? _beforeEditMemento;

    public void BeginEdit()
    {
        if ( this._beforeEditMemento != null )
        {
            throw new InvalidOperationException();
        }

        this._beforeEditMemento = this.SaveToMemento();
    }

    public void CancelEdit()
    {
        if ( this._beforeEditMemento == null )
        {
            throw new InvalidOperationException();
        }

        this.RestoreMemento( this._beforeEditMemento );
    }

    public void EndEdit()
    {
        if ( this._beforeEditMemento == null )
        {
            throw new InvalidOperationException();
        }

        this._beforeEditMemento = null;
    }

    public abstract IMemento SaveToMemento();

    public abstract void RestoreMemento( IMemento memento );
}
```

## What are the common problems of the Memento pattern?

The Memento pattern is very useful. However, when deciding whether to use it and how to implement it in detail, there are some challenges to keep in mind.

### Problem 1. Working with object graphs

The Memento pattern has been formalized to take snapshots of a single class instance. However, in most practical applications, we will work not with isolated object instances but with object _graphs_. And we will want to take a memento of the whole graph.

This problem is not addressed by the classic Memento pattern, and we see that as its major limitation.

If we want to follow the Memento pattern with multi-object operations, we will need to call the `CaptureMemento` method for all objects in the operation. We will need to improve the Caretaker to support multi-object undo operations.

```cs
void CaptureMemento( IEnumerable<IMementoable> originators );
```

It's important that all mementos captured in a single call of `CaptureMemento` are also restored in a single call of `Undo`.

### Problem 2. Memory consumption

Storing a large number of mementos can lead to significant memory usage. This is especially true for collections.

One solution is to selectively save only the necessary information, such as changes or deltas, especially on large objects. This can significantly reduce memory usage but is very challenging to implement. This solution, however, deviates from the pure Memento pattern.

Another approach is limiting the available history to a reasonable number of changes by removing the oldest mementos when the history contains more than this limit.

### Problem 3. Working with collections

Dealing with mutable collections within the Originator's state requires careful handling to prevent inconsistencies. Simply copying the data field will result in a reference to the same collection, which may be shared between the live state and multiple mementos. This violates the purpose of the Memento pattern.

There are two ways to address this issue:
- **Deep copy**: The collection can be copied when the Memento is being captured. The problem with this approach is very high memory consumption.
- **Immutable collections**: The target class uses fully immutable collections, which are remembered by the memento as normal values.

In the sample, we have used the second approach. While there are some performance and memory costs associated with immutable collections, it's the simplest way of working with collections with the Memento pattern.

### Problem 4. State isolation during edits

Last and not least, a major drawback of the Memento pattern is that user edits will affect the "good" instance of the object instead of a "draft" one. If your application has background tasks, they will see _unconfirmed_ changes, that were not yet saved by clicking the Ok button. Writing temporary edits to a "good" instance can create serious problems because neither your background task nor the user expects unconfirmed changes to be considered.

To avoid this scenario, performing edits on a _clone_ of the object is preferable. If edits are canceled, the clone is just discarded. If edits are confirmed, a memento is taken from the clone and restored into the "good" copy.

## Conclusion

The Memento pattern is a classic and simple design pattern that has several applications in the UI layer. One of its main drawbacks is that it requires much boilerplate code to implement, but code generators such as Metalama can help reduce repetitive work to almost zero. As always with classic patterns, Memento should not be applied dogmatically. However, it remains a great source of inspiration in your journey to build reliable and user-friendly applications.

---

## Site navigation

- Index of the whole site: [llms.txt](../llms.txt)

