Alex Rivera | Logout

Immutable object pattern in C# - what do you think?

Asked 2008-11-04T21:49:02.657
47

I have over the course of a few projects developed a pattern for creating immutable (readonly) objects and immutable object graphs. Immutable objects carry the benefit of being 100% thread safe and can therefore be reused across threads. In my work I very often use this pattern in Web applications for configuration settings and other objects that I load and cache in memory. Cached objects should always be immutable as you want to guarantee they are not unexpectedly changed.

Now, you can of course easily design immutable objects as in the following example:

public class SampleElement
{
  private Guid id;
  private string name;

  public SampleElement(Guid id, string name)
  {
    this.id = id;
    this.name = name;
  }

  public Guid Id
  {
    get { return id; }
  }

  public string Name
  {
    get { return name; }
  }
}

This is fine for simple classes - but for more complex classes I do not fancy the concept of passing all values through a constructor. Having setters on the properties is more desirable and your code constructing a new object gets easier to read.

So how do you create immutable objects with setters?

Well, in my pattern objects start out as being fully mutable until you freeze them with a single method call. Once an object is frozen it will stay immutable forever - it cannot be turned into a mutable object again. If you need a mutable version of the object, you simply clone it.

Ok, now on to some code. I have in the following code snippets tried to boil the pattern down to its simplest form. The IElement is the base interface that all immutable objects must ultimately implement.

public interface IElement : ICloneable
{
  bool IsReadOnly { get; }
  void MakeReadOnly();
}

The Element class is the default implementation of the IElement interface:

public abstract class Element : IElement
{
  private bool immutable;

  public bool IsRea
Edit
Report

5 Answers

10

After my initial discomfort about the fact that I had to create a new System.Drawing.Point on each modification, I've wholly embraced the concept some years ago. In fact, I now create every field as readonly by default and only change it to be mutable if there's a compelling reason – which there is surprisingly rarely.

I don't care very much about cross-threading issues, though (I rarely use code where this is relevant). I just find it much, much better because of the semantic expressiveness. Immutability is the very epitome of an interface which is hard to use incorrectly.

answered 2008-11-04T21:58:42.027
6

The (relatively) new Software Design paradigm called Domain Driven design, makes the distinction between entity objects and value objects.

Entity Objects are defined as anything that has to map to a key-driven object in a persistent data store, like an employee, or a client, or an invoice, etc... where changing the properties of the object implies that you need to save the change to a data store somewhere, and the existence of multiple instances of a class with the same "key" imnplies a need to synchronize them, or coordinate their persistence to the data store so that one instance' changes do not overwrite the others. Changing the properties of an entity object implies you are changing something about the object - not changing WHICH object you are referencing...

Value objects otoh, are objects that can be considered immutable, whose utility is defined strictly by their property values, and for which multiple instances, do not need to be coordinated in any way... like addresses, or telephone numbers, or the wheels on a car, or the letters in a document... these things are totally defined by their properties... an uppercase 'A' object in an text editor can be interchanged transparently with any other uppercase 'A' object throughout the document, you don't need a key to distinguish it from all the other 'A's In this sense it is immutable, because if you change it to a 'B' (just like changing the phone number string in a phone number object, you are not changing the data associated with some mutable entity, you are switching from one value to another... just as when you change the value of a string...

answered 2008-11-04T22:27:30.847
4

This is an important problem, and I've love to see more direct framework/language support to solve it. The solution you have requires a lot of boilerplate. It might be simple to automate some of the boilerplate by using code generation.

You'd generate a partial class that contains all the freezable properties. It would be fairly simple to make a reusable T4 template for this.

The template would take this for input:

  • namespace
  • class name
  • list of property name/type tuples

And would output a C# file, containing:

  • namespace declaration
  • partial class
  • each of the properties, with the corresponding types, a backing field, a getter, and a setter which invokes the FailIfFrozen method

AOP tags on freezable properties could also work, but it would require more dependencies, whereas T4 is built into newer versions of Visual Studio.

Another scenario which is very much like this is the INotifyPropertyChanged interface. Solutions for that problem are likely to be applicable to this problem.

answered 2010-11-03T21:05:54.483
2

Just a tip to simplify the element properties: Use automatic properties with private set and avoid explicitly declaring the data field. e.g.

public class SampleElement {
  public SampleElement(Guid id, string name) {
    Id = id;
    Name = name;
  }

  public Guid Id {
    get; private set;
  }

  public string Name {
    get; private set;
  }
}
answered 2008-11-05T12:37:53.470
2

Two other options for your particular problem that haven't been discussed:

  1. Build your own deserializer, one that can call a private property setter. While the effort in building the deserializer at the beginning will be much more, it makes things cleaner. The compiler will keep you from even attempting to call the setters and the code in your classes will be easier to read.

  2. Put a constructor in each class that takes an XElement (or some other flavor of XML object model) and populates itself from it. Obviously as the number of classes increases, this quickly becomes less desirable as a solution.

answered 2009-06-23T20:23:35.333

Your Answer