Object and collection initializers in C#

In this post I want to present a nice C# syntax sugar: object and collection initializers.

Object initializers

We will start with object initializers. Let’s assume we have classes:

public class Position {
    public int Top { get; set; }
    public int Left { get; set; }
}

public class Button {
    public Position Position { get; private set; } 
    public string Text { get; set; }
    public int FontSize { get; set; }

    public Button() {
        Position = new Position();
        FontSize = 10;
    }
}

Without object initializer we could set Button object properties using the following code:

var okButton = new Button();

okButton.Position.Top = 100;
okButton.Position.Left = 200;
okButton.Text = "OK";
okButton.FontSize = 13;

With object initializers we can shorten this code a bit:

var okButton = new Button {
    Position = {
        Top = 100,
        Left = 200
    },
    Text = "OK",
    FontSize = 13
};

Instructions generated by compiler in this two cases are exactly the same, but code using object initializer is more clear and more readable.

Now let’s dive into some syntax details:
I. We can pass any parameters to object constructor using syntax:

var foo = new Foo(param1, param2) {
    Property1 = "some value",
    Property2 = "some other value",
    // ...
}

II. When we call parameterless constructor we can omit parentheses (like I did in the Button example):

var foo = new Foo() {
    // ...
};

// this is exactly the same as new Foo()
var foo2 = new Foo {
    // ...
}

III. Object initializers can be nested:

var invoice = new Invoice {
    ShippingAddress = new Address {
        Street = "Long Street",
        Number = "3A"
    },

    InvoiceAddress = new Address {
        Street = "Short Street",
        Number = "4B"
    }
};

IV. We can set nested properties without creating new objects
Do you remember our first example with Button? We use

var okButton = new Button {
    Position = {
        Top = 100,
        Left = 200
    },
    // ...
};

to set properties of Button Position object. Notice that Position property has private setter

public Position Position { get; private set; }

so we cannot use new to create new Position object

var okButton = new Button {
    //         v doesn't compile
    Position = new Position {
        Top = 100,
        Left = 200
    },
    // ...
};

Fortunately object initializer syntax is flexible enough to allow us set properties without creating new objects.

V. Object initializer syntax works only with new
In other words following code doesn’t compile:

// doesn't compile
var foo = SomeMethodReturningFoo() {
    Property1 = 1
};

Collection initializers

Now it’s time to present collection initializers. We will start with List<T> initializers, then we will describe Dictionary<TKey, TValue> initializers.

As we all know C# allows to create and initialize arrays in single expression:

var funnyNames = new string[] { "foo", "bar", "baz", "yay" };

Wouldn’t it be nice if this syntax worked with List<T>’s? Actually thanks to collection initializers it works and not only with List’s but also with custom classes!

Let’s see an example:

List<string> funnyNames = new List<string> { "foo", "bar", "baz" };

this code is translated by compiler to

List<string> tmp = new List<string>();
tmp.Add("foo");
tmp.Add("bar");
tmp.Add("baz");

List<string> funnyNames = tmp;

Inside initializers we are not limited to constants, we can use complex expressions like

List<object> things = new List<object>() {
    (1+3*7),
    true.ToString(),
    new object()
};

As we already seen compiler translates collection initializers to a couple of Add calls. We can make collection initializers work with custom classes by providing Add method and implementing IEnumerable or IEnumerable<T>. This second requirement is very important - since we are using collection initializers, compiler expects that we will initialize some kind of collection and collections ought to implement IEnumerable interface.

Below we present minimal generic and non-generic class that works with collections initializers:

public class CustomCollection : IEnumerable {
    public void Add(object item) {
        Console.WriteLine("Add({0})", item);
    }

    IEnumerator IEnumerable.GetEnumerator() {
        yield break;
    }
}

public class CustomCollection<T> : IEnumerable<T> {
    public void Add(T item) {
        Console.WriteLine("Add<T>({0})", item);
    }

    IEnumerator<T> IEnumerable<T>.GetEnumerator() {
        yield break;
    }

    IEnumerator IEnumerable.GetEnumerator() {
        yield break;
    }
}

NOTE: We used yield break to quickly provide dummy IEnumerable implementation.
Now we can test our custom collections:

public class Program
{
    public static void Main(string[] args)
    {
        var nonGeneric = new CustomCollection { 1, 2, 3 };
        var generic = new CustomCollection<int> { 1, 2, 3 };
    }
}

When ran this program will write:

Add(1)
Add(2)
Add(3)
Add<T>(1)
Add<T>(2)
Add<T>(3)

Now since collections initializers are translated to a Add method calls, what will happen when we write Add method that take more than one parameter?

public class CustomCollection2 : IEnumerable {
    // notice *two* parameters
    public void Add(object item, object item2) {
        Console.WriteLine("Add({0}, {1})", item, item2);
    }

    IEnumerator IEnumerable.GetEnumerator() {
        yield break;
    }
}

It turns out that we still can use collection initializers, but the syntax is a bit different

var coll2 = new CustomCollection2 { 
    { 1, 1 }, 
    { 2, 2 },
    { 3, 3 } 
};

This is translated by compiler to

CustomCollection2 tmp = new CustomCollection2();
tmp.Add(1, 1);
tmp.Add(2, 2);
tmp.Add(3, 3);
CustomCollection2 coll2 = tmp;

With this knowledge it should be now easy to understand how Dictionary<TKey, TValue> initializers work. First Dictionary<TKey, TValue> defines Add(TKey, TValue) method, second it implements IEnumerable<KeyValuePair<TKey, TValue>> interface - so it fulfills all requirements needed by collection initializers to work.

We can initialize Dictionary with the following code

var dict = new Dictionary<string, string> {
    { "1", "one" },
    { "2", "two" },
    { "3", "three" }
};

Which as we already know is translated into

Dictionary<string, string> tmp = new Dictionary<string, string>();
tmp.Add("1", "one");
tmp.Add("2", "two");
tmp.Add("3", "three");

var dict = tmp;

C# 6 introduced more flexible syntax for Dictionary initializers

var dict = new Dictionary<string, string> {
    ["1"] = "one",
    ["2"] = "two",
    ["3"] = "three"
};

This is not translated to Add calls, but uses indexer instead

Dictionary<string, string> tmp = new Dictionary<string, string>();
tmp["1"] = "one";
tmp["2"] = "two";
tmp["3"] = "three";
var dict = tmp;

As other collection initializers it can work with custom types, only requirement is that type must have indexer

public class CustomCollection {
    public object this[string key] {
        get { return null; }
        set { /* do something with value */ }
    }
}

// usage:
var coll = new CustomCollection {
    ["foo"] = 1,
    ["bar"] = 2
};

Another feature is that we can set properties of already existing objects in collection, for example let’s assume that constructor of our collection already added some objects into collection:

public class Example : Dictionary<string, Position> {
    public Example() {
        Add("topLeft", new Position());
        Add("bottomRight", new Position());
    }
}

Then we can write

var example  = new Example {
    ["topLeft"] = { Top = 10, Left = 20 },
    ["bottomRight"] = { Top = 100, Left = 220 }
};

This will be translated by compiler into

Example tmp = new Example();
tmp["topLeft"].Top = 10;
tmp["topLeft"].Left = 20;
tmp["bottomRight"].Top = 100;
tmp["bottomRight"].Left = 220;
Example example = tmp;

The last thing worth know about this new initializer syntax is ability to mix it with property initializers e.g.

 var example  = new Example {
    ["topLeft"] = { Top = 10, Left = 20 },
    ["bottomRight"] = { Top = 100, Left = 220 },

    // normal property
    Tag = new object()
};

That was plenty of knowledge, but the best way to learn about initializer is to use them in code. After a while they become second nature for C# programmers and central part of many language idioms.

marcin-chwedczuk

A Programmer, A Geek, A Human