Difference between Collection Initializer Syntax

MSDN claims that:

Using the collection initializer, you do not need to specify several calls to the add class method in the source code; the compiler adds calls.

They also give this example using the new syntax to initialize a collection using parentheses:

var numbers = new Dictionary<int, string> { [7] = "seven", [9] = "nine", [13] = "thirteen" }; 

However, when checking the generated IL code, it seems that this code does not lead to calls to the Add method at all, but rather to one set_item , for example:

 IL_0007: ldstr "seven" IL_000c: callvirt instance void class [mscorlib]System.Collections.Generic.Dictionary`2<int32, string>::set_Item(!0/*int32*/, !1/*string*/) 

The "old" curly brackets syntax, in contrast, gives the following:

 // C# code: var numbers2 = new Dictionary<Int32, String> { {7, "seven"}, {9, "nine"}, {13, "thirteen"} }; // IL code snippet: // ---------- // IL_0033: ldstr "seven" // IL_0038: callvirt instance void class [mscorlib]System.Collections.Generic.Dictionary`2<int32, string>::Add(!0/*int32*/, !1/*string*/) 

... As you can see, the Add call is the result, as expected. (One can only assume that the text on MSDN mentioned above has not yet been updated.)

I have so far discovered one case where this difference really matters, and this is with the quirky System.Collections.Specialized.NameValueCollection . This allows a single key to point to multiple values. Initialization can be performed in both directions:

 const String key = "sameKey"; const String value1 = "value1"; const String value2 = "value2"; var collection1 = new NameValueCollection { {key, value1}, {key, value2} }; var collection2 = new NameValueCollection { [key] = value1, [key] = value2 }; 

... But due to differences in how the first one actually calls NameValueCollection::Add(string, string) , the results differ when viewing the contents of each collection;

collection1 [key] = "value1, value2"

collection2 [key] = "value2"

I understand that there is a connection between the old syntax and the IEnumerable interface, and how the compiler finds the Add method by naming convention, etc. I also understand the benefits of any type of indexer subject to the new syntax, as discussed in this SO answer earlier.

Perhaps these are all the expected functions, from your point of view, but the consequences for me did not matter, and I am curious to know more.

So, I am wondering if there is a documentation source on MSDN or elsewhere that clarifies this difference in behavior that comes with a choice of syntax. I also wonder if you know of other examples where this choice can have the same effect as when initializing NameValueCollection .

+5
source share
1 answer

I believe that for a final clarification you need to go to the specification. The C # 6 specification is not "officially" released, but there is an unofficial draft .

Interestingly, despite its location in the Programming Guide, the indexer syntax is not a collection initializer, it is an object initializer. From 7.6.11.3 "Collection Initializers" :

The collection initializer consists of a sequence of element initializers enclosed by {and} tokens and separated by commas. Each element initializer indicates an element to be added to the initialized collection object, and consists of a list of expressions enclosed by tokens and separated by commas .... The collection object to which the collection initializer is applied must have a type that implements System. Collections.IEnumerable or compile-time error. For each specified element in order, the collection initializer calls the Add method on the target with a list of element initializer expressions as a list of arguments

And from 7.6.11.2 'Object Intializers' :

An object initializer consists of a sequence of element initializers enclosed with {and} tokens and separated by commas. Each member_initializer designates a target for initialization. The identifier must indicate the available field or property of the initialized object, while the argument list, enclosed in square brackets, must indicate the arguments for the available indexer for the initialized object.

Take this as an example:

 public class ItemWithIndexer { private readonly Dictionary<string, string> _dictionary = new Dictionary<string, string>(); public string this[string index] { get { return _dictionary[index]; } set { _dictionary[index] = value; } } } 

Note that this class does not satisfy the requirements for using the collection initializer: it does not implement IEnumerable or has the Add method, so any attempt to initialize this way will result in a compile-time error.However, this object initializer aimed at the indexer will compile and work (see .this script ):

 var item = new ItemWithIndexer { ["1"] = "value" }; 
+4
source

All Articles