8

Will compiler optimize this code or will the collection be initialized after each method call?

private string Parse(string s)
{
    var dict = new Dictionary<string, string>
    {
        {"a", "x"},
        {"b", "y"}
    };

    return dict[s];
}

If answer is negative, I recommend to use this solution: Creating a constant Dictionary in C#

Community
  • 1
  • 1
astef
  • 8,575
  • 4
  • 56
  • 95

3 Answers3

13

When such a question arises and you're not sure what the answer is, it's always good to look "under the hood".

This is the IL the compiler generated with the optimizer turned on:

Parse:
IL_0000:  newobj      System.Collections.Generic.Dictionary<System.String,System.String>..ctor
IL_0005:  stloc.1     // <>g__initLocal0
IL_0006:  ldloc.1     // <>g__initLocal0
IL_0007:  ldstr       "a"
IL_000C:  ldstr       "x"
IL_0011:  callvirt    System.Collections.Generic.Dictionary<System.String,System.String>.Add
IL_0016:  ldloc.1     // <>g__initLocal0
IL_0017:  ldstr       "b"
IL_001C:  ldstr       "y"
IL_0021:  callvirt    System.Collections.Generic.Dictionary<System.String,System.String>.Add
IL_0026:  ldloc.1     // <>g__initLocal0
IL_0027:  stloc.0     // dict
IL_0028:  ldloc.0     // dict
IL_0029:  ldarg.1     
IL_002A:  callvirt    System.Collections.Generic.Dictionary<System.String,System.String>.get_Item
IL_002F:  ret    

As you can see, it calls newobj to allocate the Dictionary<K,V> each time, loads both locals and calls Dictionary.Add each time on both of them (which is the syntactical sugar equivalent of calling Add) . It has no intimate knowledge with the type in order to cache the objects creation.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • 2
    The result of this inspection might change at any time. It is much better to reason that the compiler is not allowed to do this ever. – usr Nov 14 '14 at 16:52
  • @usr You're right, that's why i stated the compiler has no intimate knowledge with the type. Although saying *"inspection might change at any time"* is a rather general statement, which can be true for any of the answers below. – Yuval Itzchakov Nov 14 '14 at 17:08
6

No, it will not. It has no intrinsic knowledge of what a Dictionary is. For the compiler, it's just a normal class, so it doesn't know it could reuse the instance in this particular case.

This would be the proper way to do this:

public class Something
{
    private static readonly Dictionary<string, string> _dict = new Dictionary<string, string>
    {
        {"a", "x"},
        {"b", "y"}
    }

    private string Parse(string s)
    {
        return _dict[s];
    }
}

This approach works because you know what the object does and you also know it's never modified.

Remember the following syntax:

var dict = new Dictionary<string, string>
{
    {"a", "x"},
    {"b", "y"}
}

Is just syntactic sugar for this:

var dict = new Dictionary<string, string>();
dict.Add("a", "x");
dict.Add("b", "y");

The only requirement to make this work with any class, is for the class to

  • Implement IEnumerable
  • Have a public Add method.

You suggested to use a switch statement, but the Dictionary approach can be more flexible. Consider for instance you could want to use a different equality comparer (like StringComparer.OrdinalIgnoreCase). It's better to let the Dictionary handle this using the proper comparer than using something like switch(value.ToLowerInvariant()).

Lucas Trzesniewski
  • 50,214
  • 11
  • 107
  • 158
  • There are many examples when compiler depends on specific .NET classes. Why not `Dictionary`? – astef Nov 14 '14 at 08:51
  • @astef why should it? What's so special about a `Dictionary`? Why not a `HashSet` then? or `OrderedDictionary`, or `ConditionalWeakTable`? This would be endless. It's better to have only a few special known cases (`Task` comes to mind) – Lucas Trzesniewski Nov 14 '14 at 08:55
  • What so special in `Add` method to replace it with initializer? Honestly, I don't know. Just asking ) – astef Nov 14 '14 at 10:42
  • @astef it's simply a convention for developer convenience. When the compiler sees an initializer it will replace it with `Add` calls but there's nothing special about the method itself. It could do anything. The compiler doesn't care, it just provides syntactic sugar for you. – Lucas Trzesniewski Nov 14 '14 at 11:04
2

No, C# as it stands won't 'optimize' this in any way - and I seriously doubt that will ever be the case.

While you may argue that this particular code could be optimized, it is in fact a border case, which is not that trivial to determine at compile time. To give you a counter example - what if one of your string literals was a member of another class? What if you had a custom dictionary (and there is no special handling for the existing dictionary class) which did something funky in its constructor?

decPL
  • 5,384
  • 1
  • 26
  • 36
  • "what if one of your string literals was a member of another class?" - can't see anything bad in it, can you explain? You right about custom dictionaries, but what about usual dictionary. It is already coupled with a compiler (see initializer) – astef Nov 14 '14 at 08:48
  • Well imagine you had `new Dictinary { { "x", SOME_CLASS.MEMBER } }`. Now when `SOME_CLASS.MEMBER` changes, a consequent call to `Parse` should use the new value - so it can't be optimized. – decPL Nov 14 '14 at 08:57
  • I think you're misunderstanding reference types in C#. Possible optimization is just making dictionary a static field - it will be initialized only once. What problem do you see in this case? – astef Nov 14 '14 at 09:02
  • Do I? Here's a quick and dirty example then to illustrate my point: http://pastebin.com/pYvXe1bc – decPL Nov 14 '14 at 09:21
  • Now I get what you mean. Of course, this case with run-time value defining can't be optimized. Though, it can be easily detected – astef Nov 14 '14 at 10:39
  • I disagree - what if the value was for example readonly? What if it was a fixed lambda that returns a const literal? Obviously it's doable - there's no magic there, but there's a ton of scenarios to consider - just to do something that would be counterintuitive to your average dev. As I've mentioned earlier - the compiler can't try to out-think the developer - if you're instantiating a dictionary on each method call, you must have had your reasons. – decPL Nov 14 '14 at 11:37