Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will compiler optimize collections initialization?

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#

like image 334
astef Avatar asked Nov 14 '14 08:11

astef


2 Answers

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.

like image 177
Yuval Itzchakov Avatar answered Sep 18 '22 23:09

Yuval Itzchakov


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()).

like image 6
Lucas Trzesniewski Avatar answered Sep 19 '22 23:09

Lucas Trzesniewski