Docs: Index Initializers section is inaccurate

Created on 4 May 2018  Â·  10Comments  Â·  Source: dotnet/docs

In the Index initializers section, the initial text about collection initializers seems to imply that there was no way to initialize a Dictionary<,> prior to the new C# 6.0 syntax. This is incorrect. The following code is semantically equivalent to the index initializer example in the document.

private Dictionary<int, string> webErrors = new Dictionary<int, string>
{
    { 404, "Page not Found" },
    { 302, "Page moved, but left a forwarding address." },
    { 500, "The web server can't come out to play today." }
};

Document Details

⚠ Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

Area - C# Guide P1 up-for-grabs

Most helpful comment

@pkulikov
Yes, the new language is inclusive of collection initializers for dictionaries.

There is still a problem, however, Collection Initializers require the type to implement IEnumerable, and work by calling the appropriate Add method on the object. The syntax is extensible to collections of arity greater than 2, and even to classes with Add methods of mixed arity. The following compiles and runs:

class Foo : IEnumerable {
    public void Add (string s1, string s2) => Console.WriteLine($"{s1}{s2}");
    public void Add (int x, char y, string z) => Console.WriteLine($"{x} {y} {z}");
    IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
}

new Foo { 
    { "foo", "bar" },
    { 1, '1', "1" }, 
    { 2, '4', "8" }, 
    { 3, '9', "27" }
};

The new Dictionary initialization syntax is actually an Object Initializer which works by setting the appropriate field, property or indexer. This is shown by the following code:

class Foo {
    public string bar;
    public double Baz { set => Console.WriteLine($"Baz set to {value}"); }
    public char this[int i] { set => Console.WriteLine($"Set index {i} to {value}"); }
    public string this[char c, int i] { set => Console.WriteLine($"Set index ({c},{i}) to {value}"); }
}

new Foo {
    bar = "bar1",
    [1] = '1',
    [2] = '4',
    [3] = '9',
    Baz = Math.PI,
    ['C',4] = "Middle C"
}

So, the new dictionary initialization syntax is not a new form of collection initializer, but arises out of indexer support in object initializers. This is also seen by viewing the IL generated by the above code.

Not sure how far down this rabbit hole you want to go, but I think that clarifying that the new syntax is actually an object initializer would help with understanding of what is actually going on.

All 10 comments

We should soften the language on this topic. The new syntax is more clear, but the above example does work in earlier versions of C#

@BillWagner if my understanding is correct, this issue was fixed by #5577

@pkulikov
Yes, the new language is inclusive of collection initializers for dictionaries.

There is still a problem, however, Collection Initializers require the type to implement IEnumerable, and work by calling the appropriate Add method on the object. The syntax is extensible to collections of arity greater than 2, and even to classes with Add methods of mixed arity. The following compiles and runs:

class Foo : IEnumerable {
    public void Add (string s1, string s2) => Console.WriteLine($"{s1}{s2}");
    public void Add (int x, char y, string z) => Console.WriteLine($"{x} {y} {z}");
    IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
}

new Foo { 
    { "foo", "bar" },
    { 1, '1', "1" }, 
    { 2, '4', "8" }, 
    { 3, '9', "27" }
};

The new Dictionary initialization syntax is actually an Object Initializer which works by setting the appropriate field, property or indexer. This is shown by the following code:

class Foo {
    public string bar;
    public double Baz { set => Console.WriteLine($"Baz set to {value}"); }
    public char this[int i] { set => Console.WriteLine($"Set index {i} to {value}"); }
    public string this[char c, int i] { set => Console.WriteLine($"Set index ({c},{i}) to {value}"); }
}

new Foo {
    bar = "bar1",
    [1] = '1',
    [2] = '4',
    [3] = '9',
    Baz = Math.PI,
    ['C',4] = "Middle C"
}

So, the new dictionary initialization syntax is not a new form of collection initializer, but arises out of indexer support in object initializers. This is also seen by viewing the IL generated by the above code.

Not sure how far down this rabbit hole you want to go, but I think that clarifying that the new syntax is actually an object initializer would help with understanding of what is actually going on.

@drewjcooper , you are awesome. Great explanation as well. Let me find some time to clarify it better since I started working down the issue. I will focus on making sure there is a distinction made between the fact that the previous method is a collection initializer using the IEnumerable implementation and the new index initializer utilizes the indexer support as an object initializer for index assignment during initialization.

@drewjcooper cool examples, thank you! Indeed, the section in question talks about collection initializers while not mentioning object initializers at all. Do I understand you correctly that you suggest that section on index initializers should mention object initializers being extended, while collection initializer words might (or even should) be omitted.

And just for fun of walking the rabbit holes, the collection initializers with dictionaries still might be useful to catch mistakes:

    var dict1 = new Dictionary<int, string>
    {
      [0] = "Zero",
      [0] = "Nihil"
    };
    var dict2 = new Dictionary<int, string>
    {
      { 0, "Zero" },
      { 0, "Nihil"}
    };

dict1 has one element, while initializing dict2 throws ArgumentException

@pkulikov and @BillWagner I will try to simplify the details a little for incorporation into the documentation as noted above by @drewjcooper . I think it is an important concept to hit the wave tops of without getting overly complex. Let me know if issue #5169 is something you want to keep open a little longer, otherwise I will hold off.

Also off topic, @BillWagner I did not even realize who you were until I was looking through my Safari books queue yesterday and saw the effective and more effective books ha ha. I was like, I have seen that name recently. Pleasure to assist.

@DevonAleshire if you want to contribute further to fixing this issue, ask the maintainers to assign the issue to you. That way, other contributors know that somebody is working on the issue and don't grab it (this one has the up-for-grabs tag, which means it shouts to the community contributors for attention).

Furthermore, the issue doesn't seem to be urgent and is of the medium priority (notice the P2 tag). So, take your time to think about it and come up with the good wording; really, no hurry. However, don't feed your perfectionist self and make a PR as soon as you start rewriting text in circles; the PR reviews and feedback really help and sometimes are eye-openers.

If during the process, something happens that make you stop working on this issue (long hours in office for the six-months-ahead deadline, a new hobby, a sudden trip to Tibet for a year, or just lost fun), please notify maintainers to un-assign the issue from you. That's fine.

@pkulikov yes, I think the section should focus on object initializers, because that's where the indexer support is in the language. It really has nothing to do with collection initializers, except that either can be used to initialize a dictionary, or other collection type with a sparse index.

It's taken me a while to get to this, but 🥇 to @drewjcooper for making me re-read that section of the C# 6 draft spec to make sure I updated this correctly.

Updating this sprint because I'm updating the same article for P1 work.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mekomlusa picture mekomlusa  Â·  3Comments

sdmaclea picture sdmaclea  Â·  3Comments

svick picture svick  Â·  3Comments

FrancescoBonizzi picture FrancescoBonizzi  Â·  3Comments

ike86 picture ike86  Â·  3Comments