Press "Enter" to skip to content

Memento Design Pattern in C#

Last updated on September 15, 2023

If you prefer to view this in a video, click here:

 

What it is

For non-programmers, a “memento” is something that helps you remember the past.

In programming, the Memento Design Pattern lets you save an object’s values, at a specific time, so you can recall them later. The “memento” is sometimes also called a “snapshot” – a picture of an object’s property values, at a certain time.

 

Why you’d want to use it

The most common use for this pattern is when you want the user to be able to edit an object’s value, but give them the option to “undo” their changes – and revert to the original values.

This is also used in state machines – when an object goes through several state changes.  When you use it this way, you could save a list of memento objects – one for each state change. Then, you can look at the history of the object, and (if needed) revert back several levels. For this situation, think of a shipment that goes from Ordered, to Packing, to Ready for Shipment, to Shipped, to Delivered.

 

Examples

 

VERY SIMPLE MEMENTO IMPLEMENTATION

This Customer class saves its original values (that were passed in through the constructor), so we can undo any changes to it.

 

namespace DesignPatternDemos.Memento
{
    public class CustomerSimpleMemento
    {
        // These are the memento variables, which hold the original values.
        private readonly string _originalName;
        private readonly string _originalAddress;
        private readonly string _originalCity;
        private readonly string _originalStateProvince;
        private readonly string _originalPostalCode;
        public int ID { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public string StateProvince { get; set; }
        public string PostalCode { get; set; }
        // Using the memento, to detect if the object has any changes.
        public bool IsDirty
        {
            get
            {
                return Name != _originalName ||
                       Address != _originalAddress ||
                       City != _originalCity ||
                       StateProvince != _originalStateProvince ||
                       PostalCode != _originalPostalCode;
            }
        }
        public CustomerSimpleMemento(int id, string name, 
            string address, string city, string stateProvince, string postalCode)
        {
            ID = id;
            Name = name;
            Address = address;
            City = city;
            StateProvince = stateProvince;
            PostalCode = postalCode;
            // Save the originally-passed values to the "memento" variables.
            _originalName = name;
            _originalAddress = address;
            _originalCity = city;
            _originalStateProvince = stateProvince;
            _originalPostalCode = postalCode;
        }
        public void RevertToOriginalValues()
        {
            Name = _originalName;
            Address = _originalAddress;
            City = _originalCity;
            StateProvince = _originalStateProvince;
            PostalCode = _originalPostalCode;
        }
    }
}

 

After the constructor sets all the Customer object’s properties, it saves all the original values to private variables – the memento variables.

In this example. We have an IsDirty property, which can be used to see if any of the property values have been changed on the object.

If the user ever clicks the “Undo” button (on our imaginary program), the RevertToOriginal method will reset the Customer object’s properties to its original values.

 

CLEANER, MORE COMMON MEMENTO IMPLEMENTATION

In this version, instead of having separate variables for each property’s original value, they are all passed to a memento object. And that object is saved as a private variable.

This isn’t very different from the first method. Both techniques store the original values in private variables. However, I prefer this method. You have fewer private variables, which makes the code easier to read. This way is also easier if you ever want to record multiple snapshots of the Customer object. That would let you easily add the ability for a user to “undo” multiple levels.

 

namespace DesignPatternDemos.Memento
{
    public class CustomerBetterMemento
    {
        // This is the memento object, which holds the original values.
        private readonly CustomerMemento _customerMemento;
        public int ID { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public string StateProvince { get; set; }
        public string PostalCode { get; set; }
        public bool IsDirty
        {
            get
            {
                return Name != _customerMemento.Name ||
                       Address != _customerMemento.Address ||
                       City != _customerMemento.City ||
                       StateProvince != _customerMemento.StateProvince ||
                       PostalCode != _customerMemento.PostalCode;
            }
        }
        public CustomerBetterMemento(int id, string name, string address, 
            string city, string stateProvince, string postalCode)
        {
            ID = id;
            Name = name;
            Address = address;
            City = city;
            StateProvince = stateProvince;
            PostalCode = postalCode;
            // Save the originally-passed values to the "memento".
            _customerMemento = new CustomerMemento(name, address, city, stateProvince, postalCode);
        }
        public void RevertToOriginalValues()
        {
            Name = _customerMemento.Name;
            Address = _customerMemento.Address;
            City = _customerMemento.City;
            StateProvince = _customerMemento.StateProvince;
            PostalCode = _customerMemento.PostalCode;
        }
        // This is one of the rare cases where you might declare more than one class in a file.
        // The CustomerMemento class will never be used any place, other than in the Customer class.
        // So, you can make it a private class inside the one class where it's used.
        // Or, you could put it in its own file, and declare it an internal or public class.
        private class CustomerMemento
        {
            public string Name { get; private set; }
            public string Address { get; private set; }
            public string City { get; private set; }
            public string StateProvince { get; private set; }
            public string PostalCode { get; private set; }
            public CustomerMemento(string name, string address, 
                string city, string stateProvince, string postalCode)
            {
                Name = name;
                Address = address;
                City = city;
                StateProvince = stateProvince;
                PostalCode = postalCode;
            }
        }
    }
}

 

Notice that the CustomerMemento class is declared inside the Customer class, as a private class. That means CustomerMemento can only be used inside the Customer class – just like a private variable or method can only be used inside the class where it is declared.

Normally, I do not like declaring more than one class inside a file. However, CustomerMemento will never be used anyplace outside of the Customer class. So, this is one of the rare exceptions to the rule of “one class per file”. However, you could put CustomerMemento in its own file, if you wanted.

 

MULTIPLE MEMENTO IMPLEMENTATION

Here’s how to use a memento to track multiple snapshots of an object’s values. Instead of using a single private memento variable, we’ll use a private list of memento objects.

 

using System.Collections.Generic;
using System.Linq;
namespace DesignPatternDemos.Memento
{
    public class CustomerMultipleMemento
    {
        // Save a list of memento objects, to allow for multiple "snapshots" of the Customer object.
        private readonly List<CustomerMemento> _customerMementoes = new List<CustomerMemento>();
        public int ID { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public string StateProvince { get; set; }
        public string PostalCode { get; set; }
        public CustomerMultipleMemento(int id, string name, string address, 
            string city, string stateProvince, string postalCode)
        {
            ID = id;
            Name = name;
            Address = address;
            City = city;
            StateProvince = stateProvince;
            PostalCode = postalCode;
            // Save the originally-passed values to the memento list.
            SaveMemento();
        }
        public void SaveMemento()
        {
            CustomerMemento customerMemento = 
                new CustomerMemento(Name, Address, City, StateProvince, PostalCode);
            _customerMementoes.Add(customerMemento);
        }
        public void RevertToOriginalValues()
        {
            // Get the first memento, if there is one (there always should be at least one).
            CustomerMemento firstMemento = _customerMementoes.FirstOrDefault();
            // Check for null, just to be safe.
            if(firstMemento != null)
            {
                SetPropertyValuesFromMemento(firstMemento);
                // Remove all the mementoes, except for the first one.
                if(_customerMementoes.Count > 1)
                {
                    _customerMementoes.RemoveRange(1, _customerMementoes.Count - 1);
                }
            }
        }
        public void RevertToLastValues()
        {
            // Get the last memento, if there is one (there always should be at least one).
            CustomerMemento lastMemento = _customerMementoes.LastOrDefault();
            // Check for null, just to be safe.
            if(lastMemento != null)
            {
                SetPropertyValuesFromMemento(lastMemento);
                // Remove the last memento, unless it's the first one.
                if(lastMemento != _customerMementoes.First())
                {
                    _customerMementoes.Remove(lastMemento);
                }
            }
        }
        private void SetPropertyValuesFromMemento(CustomerMemento memento)
        {
            Name = memento.Name;
            Address = memento.Address;
            City = memento.City;
            StateProvince = memento.StateProvince;
            PostalCode = memento.PostalCode;
        }
        private class CustomerMemento
        {
            public string Name { get; private set; }
            public string Address { get; private set; }
            public string City { get; private set; }
            public string StateProvince { get; private set; }
            public string PostalCode { get; private set; }
            public CustomerMemento(string name, string address, 
                string city, string stateProvince, string postalCode)
            {
                Name = name;
                Address = address;
                City = city;
                StateProvince = stateProvince;
                PostalCode = postalCode;
            }
        }
    }
}

 

When the object is instantiated, the constructor calls the SaveMemento function, to create a memento object, and add it to the memento list. Whenever you call SaveMemento, it will create a new memento object, with the Customer object’s current values, and add it to the list.

RevertToOriginalValues will get the first memento, reset the Customer’s properties to that memento’s values, and remove all mementoes (other than the first one).

RevertToLastValues will get the most recent memento, reset the Customer’s properties to that memento’s values, and remove that memento from the list – unless it’s the first one.

With this technique, you can add an “undo” button to your program, and let the users go back through previous changes.

 

Where I’ve found it useful

I’ve worked on a few programs where we wanted to let users undo their changes, and we used the memento design pattern.

For the simple objects, we often used the first method, with private variables for each property. But, when the objects get larger (have more properties), I prefer to use a separate memento object.

 

All my design pattern lessons

Source code for my design pattern lessons

One Comment

  1. Bmo
    Bmo January 18, 2019

    Great tutorial. Thanks 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *