Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 IsReadOnly
  {
    get { return immutable; }
  }

  public virtual void MakeReadOnly()
  {
    immutable = true;
  }

  protected virtual void FailIfImmutable()
  {
    if (immutable) throw new ImmutableElementException(this);
  }

  ...
}

Let's refactor the SampleElement class above to implement the immutable object pattern:

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

  public SampleElement() {}

  public Guid Id
  {
    get 
    { 
      return id; 
    }
    set
    {
      FailIfImmutable();
      id = value;
    }
  }

  public string Name
  {
    get 
    { 
      return name; 
    }
    set
    {
      FailIfImmutable();
      name = value;
    }
  }
}

You can now change the Id property and the Name property as long as the object has not been marked as immutable by calling the MakeReadOnly() method. Once it is immutable, calling a setter will yield an ImmutableElementException.

Final note: The full pattern is more complex than the code snippets shown here. It also contains support for collections of immutable objects and complete object graphs of immutable object graphs. The full pattern enables you to turn an entire object graph immutable by calling the MakeReadOnly() method on the outermost object. Once you start creating larger object models using this pattern the risk of leaky objects increases. A leaky object is an object that fails to call the FailIfImmutable() method before making a change to the object. To test for leaks I have also developed a generic leak detector class for use in unit tests. It uses reflection to test if all properties and methods throw the ImmutableElementException in the immutable state. In other words TDD is used here.

I have grown to like this pattern a lot and find great benefits in it. So what I would like to know is if any of you are using similar patterns? If yes, do you know of any good resources that document it? I am essentially looking for potential improvements and for any standards that might already exist on this topic.

like image 692
Lars Fastrup Avatar asked Nov 04 '08 21:11

Lars Fastrup


People also ask

What is immutable design pattern?

In object-oriented programming, "immutable interface" is a pattern for designing an immutable object. The immutable interface pattern involves defining a type which does not provide any methods which mutate state. Objects which are referenced by that type are not seen to have any mutable state, and appear immutable.

What is immutable in C?

In object-oriented and functional programming, an immutable object (unchangeable object) is an object whose state cannot be modified after it is created. This is in contrast to a mutable object (changeable object), which can be modified after it is created.

Is Builder pattern immutable?

The builder pattern hides the complexities of creating a class. In the case of an immutable class, it can be used to avoid constructors with too many parameters. Since the builder is not immutable, the values can be set through multiple calls.

Which is a immutable object?

An object is considered immutable if its state cannot change after it is constructed. Maximum reliance on immutable objects is widely accepted as a sound strategy for creating simple, reliable code. Immutable objects are particularly useful in concurrent applications.


2 Answers

For info, the second approach is called "popsicle immutability".

Eric Lippert has a series of blog entries on immutability starting here. I'm still getting to grips with the CTP (C# 4.0), but it looks interesting what optional / named parameters (to the .ctor) might do here (when mapped to readonly fields)... [update: I've blogged on this here]

For info, I probably wouldn't make those methods virtual - we probably don't want subclasses being able to make it non-freezable. If you want them to be able to add extra code, I'd suggest something like:

[public|protected] void Freeze()
{
    if(!frozen)
    {
        frozen = true;
        OnFrozen();
    }
}
protected virtual void OnFrozen() {} // subclass can add code here.

Also - AOP (such as PostSharp) might be a viable option for adding all those ThrowIfFrozen() checks.

(apologies if I have changed terminology / method names - SO doesn't keep the original post visible when composing replies)

like image 114
Marc Gravell Avatar answered Oct 19 '22 02:10

Marc Gravell


Another option would be to create some kind of Builder class.

For an example, in Java (and C# and many other languages) String is immutable. If you want to do multiple operations to create a String you use a StringBuilder. This is mutable, and then once you're done you have it return to you the final String object. From then on it's immutable.

You could do something similar for your other classes. You have your immutable Element, and then an ElementBuilder. All the builder would do is store the options you set, then when you finalize it it constructs and returns the immutable Element.

It's a little more code, but I think it's cleaner than having setters on a class that's supposed to be immutable.

like image 17
Herms Avatar answered Oct 19 '22 01:10

Herms