foreach, с точки зрения производительности. Должны ли мы объявлять переменную один раз перед циклом или внутри него? - PullRequest
6 голосов
/ 27 октября 2009

Что лучше для производительности, объявляя переменную вне определения foreach и каждый раз переназначая ее в сторону (foreach) или создавая новую переменную внутри foreach например

private List<ListItem> GetItems()
        {
            var items = new List<ListItem>();
            var collection = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            ListItem item;
            foreach (var i in collection)
            {
                item = new ListItem { Text = i.ToString() };
                items.Add(item);
            }

            return items;
        }

или этот?

private List<ListItem> GetItems()
        {
            var items = new List<ListItem>();
            var collection = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            foreach (var i in collection)
            {
                ListItem item = new ListItem { Text = i.ToString() };
                items.Add(item);
            }

            return items;
        }

уверен, что здесь я говорю об объекте предмета. спасибо всем.

Ответы [ 8 ]

12 голосов
/ 27 октября 2009

Это звучит как преждевременная оптимизация .

Прежде всего, есть ли у вас основания полагать, что здесь есть проблема с производительностью?

Во-вторых, в релизных сборках оптимизатор компилятора, вероятно, будет генерировать идентичный код для обоих сценариев - так что он, вероятно, не имеет значения. В отладочных сборках это не всегда может быть правдой, но там вам не нужны оптимизации, поскольку цель отладочных сборок - позволить вам точно пройти по коду.

7 голосов
/ 27 октября 2009

Там - это крайний случай, когда это имеет значение; если вы «захватите» переменную в анонимный метод / лямбду. В противном случае это преждевременно и не имеет значения. На всех.

Пример того, когда это имеет значение:

// prints all items in no particular order
foreach (var i in collection)
{
    string s = i.ToString();
    ThreadPool.QueueUserWorkItem(delegate { Console.WriteLine(s); });
}

против

// may print the same item each time, or any combination of items; very bad
string s;
foreach (var i in collection)
{
    s = i.ToString();
    ThreadPool.QueueUserWorkItem(delegate { Console.WriteLine(s); });
}
4 голосов
/ 27 октября 2009

Я почти уверен, что IL, сгенерированный вашими двумя блоками кода, идентичен. Там не должно быть никаких изменений в производительности. Однако второй блок кода, где вы объявляете тип элемента прямо там, где он используется, немного более читабелен, и я бы использовал это.

3 голосов
/ 27 октября 2009

Это очень микрооптимизация, и оба метода, скорее всего, будут иметь одинаковую производительность, если не будут генерировать идентичный код. В этом случае перейдите на удобочитаемость. Я бы предпочел второе, поскольку ваш объект не имеет смысла вне цикла foreach.

Возможно, вы также можете избавиться от сохраненной ссылки все вместе:

private List<ListItem> GetItems()
{
  var items = new List<ListItem>();
  var collection = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

  foreach (var i in collection)
  {
    items.Add(new ListItem { Text = i.ToString() });
  }

  return items;
}
1 голос
/ 27 октября 2009

IL, созданный двумя блоками, должен быть почти одинаковым. Если вы хотите оптимизировать, я бы посмотрел на установку длины окончательного списка, прежде чем заполнять его элементами. Таким образом, вы не будете наказывать за расширение длины списка.

Что-то вроде:

  private List<ListItem> GetItems()
    {
        var collection = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        var items = new List<ListItem>(collection.Count);  //declare the amount of space here

        foreach (var i in collection)
        {
            ListItem item = new ListItem { Text = i.ToString() };
            items.Add(item);
        }

        return items;
    }
0 голосов
/ 27 октября 2009

Как и предполагали все, IL будет идентичным. Кроме того, как уже упоминали другие, не беспокойтесь о таких вещах, пока они не станут проблемой. Вместо этого спросите себя, где находится область действия этой переменной.

Область и контекст этого блока кода гораздо важнее крошечных оптимизаций производительности, которые были бы преждевременными по природе и ненужными в этом сценарии.

0 голосов
/ 27 октября 2009

Еще лучше в вашем случае это:

private List<ListItem> GetItems()        
{            
   var items = new List<ListItem>();            
   var collection = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };            
   foreach (var i in collection)            
      items.Add(new ListItem { Text = i.ToString() });                 
   return items;        
}

Зачем вообще создавать дополнительную переменную?

0 голосов
/ 27 октября 2009

Вероятно, компилируется в один и тот же код, но зачем его переопределять. Это хорошая вещь о ссылке, в данном случае пункт. Как только вы закончите с этим, вы можете назначить его другому ListItem и GC заботится об остальном.

Но с другой стороны читаемость для других программистов. Это решение, которое, безусловно, не приведет к существенному изменению производительности ваших приложений.

...