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 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
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, 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)
Comments are closed. |
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?”
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)
This kind of initilization is particularly useful if you want to return list of complex nested object with LINQ queries.
That’d be handy for initializing sets with a custom comparer.
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)