Immutable objects with object initialisers

c#

Solution

You can't use object initializers with immutable objects. They require settable properties.

An immutable object implies "does not change after creation". Making `Name` a constructor parameter neatly expresses that principle.

If the object gets too complicated for a comprehensible constructor, you can also use the Builder pattern. Generally, the builder itself will have mutable properties (that you can use in object initializers), and its .Build() method will create the actual instance.

EDIT (OP): I'm going to add my own example of a builder that I cooked up here, then accept this answer since it proposes a reasonable solution.

class MyObject
{
    public class Builder
    {
        public Builder()
        {
            // set default values
            Name = String.Empty;
        }

        public MyObject Build()
        {
            return new MyObject(Name);
        }
        public string Name { get; set; }
    }

    private static int nextId;

    protected MyObject(string name)
    {
        Id = ++nextId;
        Name = name;
    }

    public int Id { get; private set; }
    public string Name { get; private set; }
}

You can then construct an instance of it with the following:

MyObject test = new MyObject.Builder { Name = "foo" }.Build();

EDIT: This is my take on the pattern:

public abstract class Builder<T>
{
    public static implicit operator T(Builder<T> builder)
    {
        return builder.Build();
    }

    private bool _built;

    public T Build()
    {
        if(_built)
        {
            throw new InvalidOperationException("Instance already built");
        }

        _built = true;

        return GetInstance();
    }

    protected abstract T GetInstance();
}

Here is your example as implemented with `Builder<T>`. It takes advantage of the scoping rules of nested types to access the private setter:

public class MyObject
{
    private static int nextId;

    protected MyObject()
    {
        Id = ++nextId;
    }

    public int Id { get; private set; }

    public string Name { get; private set; }

    public sealed class Builder : Builder<MyObject>
    {
        private MyObject _instance = new MyObject();

        protected override MyObject GetInstance()
        {
            // Validate properties here

            return _instance;
        }

        public string Name
        {
            get { return _instance.Name; }
            set { _instance.Name = value; }
        }
    }
}

It has an implicit conversion to the target type, allowing you to do this:

MyObject myObject = new MyObject.Builder { Name = "Some name" };

Or this:

public void Foo(MyObject myObject)

// ...

Foo(new MyObject.Builder { Name = "Some name" });

Problem

I have the following attempt at an immutable object: ``` class MyObject { private static int nextId; public MyObject() { _id = ++nextId; } private int _id; public int Id { get { return _id; } } public string Name { get; private set; } } ``` Then, I try to use it like so: ``` MyObject o1 = new MyObject { Name = "foo" }; ``` But the object initialiser fails because `Name`'s setter is private. Is there a way around this, or do I have to choose between one or the other?

Original source