2014-11-14 3 views
8

Будет ли компилятор оптимизировать этот код или будет инициализирован сбор после каждого вызова метода?Будет ли компилятор оптимизировать инициализацию коллекций?

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

    return dict[s]; 
} 

Если ответ отрицательный, то я рекомендую использовать это решение: Creating a constant Dictionary in C#

+0

Хорошо, что GC будет удалять его после возможности метода, хотя он не детерминирован. – DevEstacion

+0

Можете ли вы объяснить, почему вы думаете, что компилятор может оптимизировать это и как? – CodeCaster

+1

@CodeCaster Почему: компилятор может зависеть от определенных классов .NET, и эта ситуация может быть легко обнаружена. Как: сделать его статическим, например – astef

ответ

13

Когда возникает такой вопрос, и вы не знаете, что такое ответ, всегда хорошо выглядеть «под капотом».

Это IL компилятор генерируется с оптимизатором включен:

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  

Как вы видите, он вызывает newobj выделить Dictionary<K,V>каждый раз, грузы как местных жителей и вызывает Dictionary.Add каждый раз, когда на оба они (что является синтаксическим эквивалентом сахара вызова Add). Он не имеет интимных знаний с типом, чтобы кэшировать создание объектов.

+2

Результат этой проверки может измениться в любое время. Гораздо лучше рассуждать о том, что компилятору не разрешено делать это когда-либо. – usr

+0

@usr. Вы правы, поэтому я сказал, что у компилятора нет интимных знаний с типом. Хотя высказывание * «проверка может измениться в любое время» * является довольно общим утверждением, что может быть справедливо для любого из приведенных ниже ответов. –

2

Нет, C#, как она стоит не «оптимизировать» это никак - и я серьезно сомневаюсь, что когда-нибудь будет дело.

Хотя вы можете утверждать, что этот конкретный код может быть оптимизирован, на самом деле это пограничный случай, который не так тривиально определить во время компиляции. Чтобы дать вам встречный пример - что, если один из ваших строковых литералов был членом другого класса? Что делать, если у вас есть пользовательский словарь (и нет специальной обработки для существующего словарного класса), который сделал что-то фанки в своем конструкторе?

+0

«Что, если один из ваших строковых литералов был членом другого класса?» - Не вижу в этом ничего плохого, не могли бы вы объяснить? Вы правы о пользовательских словарях, но как насчет обычного словаря. Он уже связан с компилятором (см. Инициализатор). – astef

+0

. Представьте, что у вас есть 'new Dictinary {{x, SOME_CLASS.MEMBER}}'. Теперь, когда изменяется 'SOME_CLASS.MEMBER', последовательный вызов' Parse' должен использовать новое значение, поэтому его нельзя оптимизировать. – decPL

+0

Я думаю, что вы недопонимаете ссылочные типы в C#. Возможная оптимизация просто делает словарь статическим полем - он будет инициализирован только один раз. Какую проблему вы видите в этом случае? – astef

6

Нет, это не так. Он не имеет никаких внутренних знаний о том, что такое Dictionary. Для компилятора это обычный класс, поэтому он не знает его может повторить использование экземпляра в этом конкретном случае.

Это будет правильный способ сделать это:

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]; 
    } 
} 

Этот подход работает, потому что вы знаете, что делает объект, и вы также знаете, что никогда не изменяется.

Помните следующий синтаксис:

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

ли просто синтаксический сахар для этого:

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

Единственное требование, чтобы сделать эту работу с любого класса, для класса в

  • Внедрение IEnumerable
  • Имейте общедоступный метод Add.

Вы предложили использовать инструкцию switch, но подход Dictionary может быть более гибким. Рассмотрим, например, вы могли бы использовать другой сопоставитель равенства (например, StringComparer.OrdinalIgnoreCase). Лучше разрешить Dictionary справиться с этим с помощью соответствующего сравнения, чем использовать что-то вроде switch(value.ToLowerInvariant()).

+0

Существует много примеров, когда компилятор зависит от определенных классов .NET. Почему бы не «Словарь»? – astef

+0

@astef почему? Что особенного в «Словаре»? Почему бы не «HashSet» тогда? или 'OrderedDictionary', или' ConditionalWeakTable'? Это было бы бесконечно. Лучше иметь только несколько специальных известных случаев ('Задача ' приходит на ум) –

+0

Что такого особенного в методе 'Добавить', чтобы заменить его инициализатором? Честно говоря, я не знаю. Просто спрашиваю) – astef

Смежные вопросы