Nifty trick: Combining constructor with collection initializer


C# provides a number of ways of initializing collections.

If a collection has a single-parameter Add method, you can add items into the collection as part of initialization:

var list = new List<int> { 1, 2, 3 };

// Equivalent to
var list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);

Note that if you do not provide an argument list after the type, one will be provided for you, namely ().

If a collection has a multi-parameter Add method, you can add items into the collection as part of the initialization, but you need to put braces around each parameter list:

var dict = new Dictionary<int, int> {
    { 0, 1 },
    { 1, 2 },
};

// Equivalent to
var dict = new Dictionary<int, int>();
dict.Add(0, 1);
dict.Add(1, 2);

If a collection has an index setter, you can add items into the collection with indexer syntax:

var dict = new Dictionary<int, int> {
    [0] = 1,
    [1] = 2,
};

// Equivalent to
var dict = new Dictionary<int, int>();
dict[0] = 1;
dict[1] = 2;

You cannot combine these different initializer notations, however.

// Code in italics doesn't compile
var dict = new Dictionary<int, int> {
    {0, 1 },
    [1] = 2,
};

However, one thing that is sometimes interesting to do is combine the constructor with the collection initializer. This lets you clone a collection and then modify it.

// Resulting list is { 1, 2, 3, 4, 5 }
var list2 = new List<int>(list) { 4, 5 };

// Resulting list is { 4, 2, 3 }
var list3 = new List<int>(list) { [0] = 4 };

// Resulting dictionary is dict2[0] = 1, dict2[1] = 2,
// and dict[2] = 3
var dict2 = new Dictionary<int, int>(dict) { [2] = 3 };

// Resulting dictionary is dict2[0] = 4, dict2[1] = 2
var dict2 = new Dictionary<int, int>(dict) { [0] = 4 };

You can pass anything that is a valid constructor parameter. For example, List<T> permits construction from any enumerable, so you can do this:

string[] ab = new string[] { "a", "b" };
List<string> abcd = new List<string>(ab) { "c", "d" };
// abcd has four elements: "a" "b" "c" and "d"

This combination notation is useful if you want to clone an existing collection and then make some tweaks to it.

Comments (5)
  1. Ian Goldby says:

    I’ve always believed (but admittedly never bothered to check) that if you write in C something like static const int lookup[] = { … long list of numbers … } then the compiler does all the heavy lifting; the array is generated in its final form as part of the executable image and there’s no runtime cost to creating and initialising the array. I suppose my first question is “is this actually the case with modern C compilers?” and my second question is “what is the C# equivalent?”

    1. SimonRev says:

      Yes, that is more or less how it works on C arrays (or C++ arrays).

      Note that a C# list is much closer to a C++ std::vector, and in C++ the code:
      std::vector myVect = { 3, 4, 5, 6} is going to create a std::inititlizer_list and run a constructor on std::vector. I am not really sure at all the details that are involved there. It is at least possible that in the case of integer literals, that the equivilent code that runs in C++ the static array as in C with a memcpy to copy the data into the std::vector.

      In C#, I am less certain what the code
      int[] x = new int[] { 1, 2, 3, 4}

      actually does. At a minimum the array would be on the heap and need to be copied out there. (since there is no such thing as a const array in C#, a copy has to be made)

  2. cheong00 says:

    This kind of initilization is particularly useful if you want to return list of complex nested object with LINQ queries.

  3. Tyler Szabo says:

    That’d be handy for initializing sets with a custom comparer.

  4. RKPatrick says:

    I like these a lot…it’s nice to learn new stuff about .Net, even after working with it for so long. I’d been using .Concat(…) to achieve the same results, but I like the semantics of the initializer quite a bit more, since it’s not building an expression and hooking into the LINQ infrastructure (it’s a bit of a heavy hand for this task)

Comments are closed.

Skip to main content