Nifty trick: Combining constructor with collection initializer

Date:November 27, 2018 / year-entry #268
Tags:code
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20181127-00/?p=100335
Comments:    5
Summary:Two great tastes that work great together.

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.


*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index