Immutable Threading Model

There are times when you want certain objects in your codebase to retain their post creation state without possibility of it ever changing. These objects are said to be immutable. Immutable objects are useful in multi-threaded applications because they can be safely accessed by several threads concurrently, without need for locking or other synchronization. PostSharp offers the ImmutableAttribute aspect that allows you enforce this pattern on your objects.

Changes in an object with the ImmutableAttribute aspect will be forbidden as soon as the object constructor exits. Any further attempt to modify the object will result in an ObjectReadOnlyException.

Note Note

The Immutable pattern can be too strong for some common object-oriented scenarios, for instance with serializable classes. In some cases, the Freezable object is a better choice. For details, see Freezable Threading Model.

This topic contains the following sections:

To make an object immutable all you need to do is add the ImmutableAttribute attribute to the class in question.

Making a class immutable using PostSharp Tools for Visual Studio
Note Note

This example shows freezing a class that only has primitive data types. Information on working with complex objects can be found the Working With Immutable Object Trees section later in this article.

To add the Immutable pattern using PostSharp Tools for Visual Studio:

  1. First place the caret on the name of the class that you want to make immutable. The smart tag will appear below the object name. Expand it and select Apply Threading Model.

    Apply Threading Model Smart Tag
  2. In the Apply Threading Model wizard select Apply immutable threading model and click Next.

    Apply Immutable

    If you have not yet added the PostSharp.Patterns.Threading package to your project, the Apply Threading Model wizard will download it from NuGet and install it into your project.

  3. Once the wizard has completed the class will now be flagged as immutable.

    C#
    using PostSharp.Patterns.Threading;
    
    [Immutable]
    public class Invoice
    {
      public Invoice(long id)
      {
        Id = id;
      }
      public long Id { get; set; }
    }

    Now, once you’ve created an instance of the immutable object your code will no longer be able to change the property values on that instance. If a value change is attempted the code will throw an ObjectReadOnlyException.

    C#
    var invoice = new Invoice(12345);
    
    // This will throw an exception.
    invoice.Id = 345678;
Making a class immutable manually

To add the Immutable pattern manually:

  1. Add the PostSharp.Patterns.Threading package to your project using NuGet.

  2. Add the ImmutableAttribute custom attribute to your class.

Immutable object trees

Because the Immutable pattern is an implementation of the Aggregatable pattern, all of the same behaviors of the AggregatableAttribute are available. As a result you can create both immutable classes and immutable object trees. For more information regarding object trees, read Parent/Child Relationships.

Note Note

Children of immutable objects must be marked as immutable themselves. Adding the ImmutableAttribute to the child classes will accomplish this. Additionally, collection types must be derived from AdvisableCollection<T> or AdvisableDictionary<TKey, TValue>.

Immutable vs readonly

Many C# developers make use of the readonly keyword in an attempt to make their objects immutable. The readonly keyword doesn't guarantee immutability though. Using readonly only ensures that no method other than the object's constructor can alter the variable's value. It doesn't, however, prevent you from altering values on complex objects outside of the constructor.

In the following code sample the _id variable is a primitive type and can't be altered outside the constructor. This is enforced at compile time and an error would be displayed where the SetIdentifier method attempts to change the _id field's value.

C#
public class Invoice
{
  public readonly long _id;
  public Invoice(long id)
  {
    SetIdentifier(id);
  }
  public void SetIdentifier(long id)
  {
    _id = id;
  }
}

If you were to mark a complex object as readonly the same rule holds true as it does for primitive types. You are not able to change the complex object instance outside of the constructor. In the following example, initializing the Customer in the constructor is valid, but reinitializing it in the Refresh method will cause a compilation error.

C#
public class Invoice
{
  public readonly Customer _customer;
  public Invoice()
  {
    _customer = new Customer();
  }

  public void Refresh()
  {
    //will cause a compilation error
    _customer = new Customer();
  }
}

What you can do when a complex object is marked as readonly is alter the values within that complex object. In the case of the Customer object you cannot reinitialize the instance but you can change the properties on the already created instance. What you see in the following Refresh methd is perfectly valid.

C#
public class Invoice
{
  public readonly Customer _customer;
  public Invoice()
  {
    _customer = new Customer();
  }

  public void Refresh()
  {
    //valid but not immutable
    _customer.Name = "Jim";
    _customer.Phone = "555-123-9876";
  }
}

The same type of change to object state can happen with collections. You can not reinitializee a readonly collection, but you can freely Add, Remove, Clear and do other operations that the collection itself exposes. Additionally if the collection contains complex types you are able to change values on each instance that the collection contains.

C#
public class Invoice
{
  public readonly IList<Item> _items;
  public Invoice()
  {
    _items = new List<Item>();
  }

  public void Refresh()
  {
    //will cause a compilation error
    _items = new List<Item>();

    //valid but not immutable
    _items.Add(new Item());
    _items[0].Price = 3.50;
    _items.RemoveAt(0);
  }
}

As you can see there is no way to use the readonly keyword to make complex object graphs immutable. Combining the ImmutableAttribute, ChildAttribute, AdvisableCollection<T> and the AdvisableDictionary<TKey, TValue> types allows you to make immutable objects that guarantee no changes to primitive or complex objects after constructor execution has completed.

Constructor execution

Objects are not frozen until the last constructor has finished executing. Because of this you can use the constructor to set up the state of the parent instance through its own constructor as well as chained, or inherited object, constructors. You're also able to make changes to child object instances through their constructors at this time.

[Immutable]
public class Invoice : Document
{
  public Invoice(long id) : base(id)
  {
    Items = new AdvisableCollection<Item>();
    Items.Add(new Item("widget"));
  }

  [Child]
  public AdvisableCollection<Item> Items { get; set; }
}

[Immutable]
public class Document
{
  private long _id;
  public Document (long id)
  {
    _id = id;
  }
}

[Immutable]
public class Item
{
  public Item (string name)
  {
    Name = name;
  }
  public string Name { get; set; }
}

In this example the constructors finish executing in the order of Document, Item and finally Invoice. It is not until after the Invoice constructor finishes executing that the object graph is made immutable.

Immutable collections

When authoring immutable object models, immutable collections are a good replacement for advisable collections. For details, see Using Immutable Collections.

See Also