Лучше ли практиковать программирование для определения переменных вне foreach, хотя и более многословно?

В следующих примерах:

  • первый кажется более многословным, но менее расточительным
  • вторая менее многословна, но более расточительна (переопределяет строку в каждом цикле)

Что является лучшей практикой кодирования?

Первый пример:

using System;
using System.Collections.Generic;

namespace TestForeach23434
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> names = new List<string> { "one", "two", "two", "three", "four", "four" };

            string test1 = "";
            string test2 = "";
            string test3 = "";
            foreach (var name in names)
            {
                test1 = name + "1";
                test2 = name + "2";
                test3 = name + "3";
                Console.WriteLine("{0}, {1}, {2}", test1, test2, test3);
            }
            Console.ReadLine();
        }
    }
}

Второй пример:

using System;
using System.Collections.Generic;

namespace TestForeach23434
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> names = new List<string> { "one", "two", "two", "three", "four", "four" };

            foreach (var name in names)
            {
                string test1 = name + "1";
                string test2 = name + "2";
                string test3 = name + "3";
                Console.WriteLine("{0}, {1}, {2}", test1, test2, test3);
            }
            Console.ReadLine();
        }
    }
}

12 ответов

Решение

Вторая форма не более расточительна - она ​​просто лучше.

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

(Обратите внимание, что обычно это не делает различий в поведении, но это не так, если переменные захватываются лямбда-выражением или анонимным методом.)

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

Это дает много преимуществ:

  1. Рефакторинг проще, так как извлечь метод проще, когда переменные уже находятся в той же области видимости.
  2. Использование переменной более понятно, что приведет к более надежному коду.

Единственным (потенциальным) недостатком было бы дополнительное объявление переменных - однако, JIT имеет тенденцию оптимизировать эту проблему, поэтому я не буду беспокоиться о ней в реальной работе.

Единственное исключение из этого:

Если ваша переменная будет добавлять большое давление ГХ, и если этого можно избежать, повторно используя один и тот же экземпляр объекта через цикл foreach/for, и если давление ГХ вызывает проблемы с измеряемой производительностью, я бы поднял его в внешняя сфера.

Это и расточительно, и многословно.

foreach (var name in names)
{
   Console.WriteLine("{0}1, {0}2, {0}3", name);
}

,

</tongueincheek>

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

Моя собственная философия в этом проста:

Оптимизируйте для простоты понимания.

Все остальное - преждевременная оптимизация! Самое большое узкое место в большинстве разработок - это время и внимание разработчика. Если вам абсолютно необходимо выжать каждый последний цикл ЦП, то непременно сделайте это, но если у вас нет острой необходимости в бизнесе или вы пишете критический компонент (общая библиотека, ядро ​​операционной системы и т. Д.), Вам лучше подождать, пока вы можно сравнить готовую программу В то время оптимизация некоторых из самых дорогостоящих процедур оправдана, а раньше это почти наверняка пустая трата времени.

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

Это моя любимая часть Linq, которая, я думаю, подходит здесь:

names.ForEach(x => Console.WriteLine("{0}1, {0}2, {0}3", x));

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

Они оба практически одинаковы с точки зрения производительности (строки неизменяемы), но что касается читабельности... Я бы сказал, что ни один из них не очень хорош. Вы можете легко сделать все это в Console.WriteLine.

Возможно, вы можете опубликовать реальную проблему вместо примера?

Я обнаружил, что "поднятие" объявлений из циклов обычно является лучшей долгосрочной стратегией обслуживания. Компилятор обычно разберется с вещами приемлемо для производительности.

Я думаю, это зависит от того, что вы пытаетесь решить. Мне нравится второй пример, потому что вы можете переместить код за один шаг. Мне нравится первый пример, потому что он быстрее из-за меньшего количества манипуляций со стеком, меньшей фрагментации памяти и меньшего количества создания / создания объектов.

Для данных типа POD объявляйте наиболее близкие к первому использованию. Для чего-либо подобного классу, который выполняет какое-либо распределение памяти, вам следует рассмотреть возможность объявления тех, которые находятся вне каких-либо циклов. Строки почти наверняка будут выполнять некоторую форму выделения, и большинство реализаций (по крайней мере, в C++) будут пытаться повторно использовать память, если это возможно. Распределение на основе кучи может быть очень медленным.

Однажды я профилировал немного кода C++, который включал класс, который new'd данные в своем ctor. С переменной, объявленной вне цикла, она работала на 17% быстрее, чем с переменной, объявленной внутри цикла. YMMV в C#, поэтому производительность профиля вы можете быть очень удивлены результатами.

Следуйте простому правилу при объявлении переменных

Объявите это, когда вам это нужно в первый раз

Другие вопросы по тегам