ChaseMedallion
ChaseMedallion

Reputation: 21794

What's the most concise way to create an immutable class in C#?

I find myself having to create a lot of immutable classes and I'd like to find a way to do it with no redundant information. I can't use an anonymous type because I need to return these classes from methods. I want intellisense support, so I'd prefer not to use Dictionaries, dynamic or anything like that. I also want well-named properties, which rules out Tuple<>. So far, some patterns I've tried:

// inherit Tuple<>. This has the added benefit of giving you Equals() and GetHashCode()
public class MyImmutable : Tuple<int, string, bool> {
   public MyImmutable(int field1, string field2, bool field3) : base(field1, field2, field3) { }

   public int Field1 { get { return this.Item1; } }
   public string Field2 { get { return this.Item2; } }
   public bool Field3 { get { return this.Item3; } }
}

///////////////////////////////////////////////////////////////////////////////////

// using a custom SetOnce<T> struct that throws an error if set twice or if read before being set
// the nice thing about this approach is that you can skip writing a constructor and 
// use object initializer syntax.
public class MyImmutable {
    private SetOnce<int> _field1;
    private SetOnce<string> _field2;
    private SetOnce<bool> _field3;


   public int Field1 { get { return this._field1.Value; } set { this._field1.Value = value; }
   public string Field2 { get { return this._field2.Value; } set { this._field2.Value = value; }
   public bool Field3 { get { return this._field3.Value; } set { this._field3.Value = value; }
}

///////////////////////////////////////////////////////////////////////////////////

// EDIT: another idea I thought of: create an Immutable<T> type which allows you to
// easily expose types with simple get/set properties as immutable
public class Immutable<T> {
    private readonly Dictionary<PropertyInfo, object> _values;       

    public Immutable(T obj) {
        // if we are worried about the performance of this reflection, we could always statically cache
        // the getters as compiled delegates
        this._values = typeof(T).GetProperties()
            .Where(pi => pi.CanRead)
            // Utils.MemberComparer is a static IEqualityComparer that correctly compares
            // members so that ReflectedType is ignored
            .ToDictionary(pi => pi, pi => pi.GetValue(obj, null), Utils.MemberComparer);
    }

    public TProperty Get<TProperty>(Expression<Func<T, TProperty>> propertyAccessor) {
        var prop = (PropertyInfo)((MemberExpression)propertyAccessor.Body).Member;
        return (TProperty)this._values[prop];
    }
}

// usage
public class Mutable { int A { get; set; } }

// we could easily write a ToImmutable extension that would give us type inference
var immutable = new Immutable<Mutable>(new Mutable { A = 5 });
var a = immutable.Get(m => m.A);

// obviously, this is less performant than the other suggestions and somewhat clumsier to use.
// However, it does make declaring the immutable type quite concise, and has the advantage that we can make
// any mutable type immutable

///////////////////////////////////////////////////////////////////////////////////

// EDIT: Phil Patterson and others mentioned the following pattern
// this seems to be roughly the same # characters as with Tuple<>, but results in many
// more lines and doesn't give you free Equals() and GetHashCode()
public class MyImmutable 
{
   public MyImmutable(int field1, string field2, bool field3)
   {
        Field1 = field1;
        Field2 = field2;
        Field3 = field3;
   }

   public int Field1 { get; private set; }
   public string Field2 { get; private set; }
   public bool Field3 { get; private set; }
}

These are both somewhat less verbose than the "standard" pattern of creating readonly fields, setting them via a constructor, and exposing them via properties. However, both of these methods still have lots of redundant boilerplate.

Any ideas?

Upvotes: 8

Views: 396

Answers (3)

supercat
supercat

Reputation: 81337

Immutable classes are most useful when there exists a corresponding mutable class which whose contents can easily be loaded from an immutable-type instance or copied to a new immutable-type instance. If one has an immutable object needs to make more than 2-3 "changes" to it (i.e. change the reference so it points to an immutable which is suitably different from the original), copying the data to a mutable object, changing it, and storing it back is often more practical than creating an object which differs from the original in one way, then creating a new object which differs from that in another way, etc.

A nice easily-generalized approach is to define a (possibly internal) exposed-field structure type, and then have both the mutable and immutable classes hold a field of that type. The accessor properties would have to be defined separately for both class types, but one could have both types' GetHashCode and Equals methods chain to the corresponding methods of the underlying struct. Creating an instance of either type given an instance of the other could be accomplished using a single struct assignment, rather than having to copy all of the members individually.

If one's programmers understand how structures work (I'd consider such knowledge fundamental, even if others disagree) one could make the structure type public. Having a mutable holder class may be useful even if the structure is public, since there are times when reference semantics are useful (e.g. one may want to be able to change something stored in a Dictionary without having to change the Dictionary itself), but the convert-to-mutable/change/convert-to-immutable pattern works even better with structure types than with class types.

Upvotes: 1

Alexei Levenkov
Alexei Levenkov

Reputation: 100630

See if public {get;private set;} properties work for your case - a bit more compact than separate field declaration, exactly the same semantic.

Update: As ChaseMedallion commented and inlined in the question this approach does not provide automatically generated GetHashCode and Equals methods unlike Tuple approach.

class MyImmutable 
{
    public int MyProperty {get; private set;}

    public MyImmutable(int myProperty)
    {
       MyProperty = v;
    }
}

I like Tuple approach as it gives object that can safely used in interesting contexts and provides nice names. If I would need to create many types of such kind I would consider to re-implement Tuple classes:

  • at construction time pre-compute GetHashCode and store as part of object to avoid unbounded checks for collections/strings. Possibly optional to allow opt in for cases that are commonly used as keys in Dictionary.
  • hide generic names (i.e. protected or just hide from intellisence with EditorBrowsableAttribute) so there is no confusion about 2 set of names.
  • consider enforcing field types to be immutable in Debug builds/FxCop rules...

Side note: check out Eric Lippert's series on immutable types.

Upvotes: 1

Phil Patterson
Phil Patterson

Reputation: 1262

You could use automatic properties with private setters

public class MyImmutable 
{
   public MyImmutable(int field1, string field2, bool field3)
   {
        Field1 = field1;
        Field2 = field2;
        Field3 = field3;
   }

   public int Field1 { get; private set; }
   public string Field2 { get; private set; }
   public bool Field3 { get; private set; }
}

Upvotes: 4

Related Questions