Лучше ли практика кодирования определять переменные за пределами 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();
}
}
}
Ответы
Ответ 1
Вторая форма не более расточительна - она просто лучше.
Невозможно объявить переменные за пределами цикла, если вы не хотите поддерживать их значения между итерациями.
(Обратите внимание, что обычно это не ведет к поведенческой разнице, но это не так, если переменные захватываются лямбда-выражением или анонимным методом.)
Ответ 2
Лично я считаю, что наилучшей практикой является объявление переменных в максимально возможной области, учитывая их использование.
Это дает много преимуществ:
- Это проще для рефакторинга, так как извлечение метода проще, когда переменные уже находятся в одной области.
- Использование переменной более понятно, что приведет к более надежному коду.
Единственным (потенциальным) недостатком будет объявление дополнительной переменной - однако JIT имеет тенденцию оптимизировать эту проблему, поэтому я бы не стал беспокоиться о ней в реальной работе.
Единственное исключение:
Если ваша переменная будет добавлять много давления GC, и, если этого можно избежать, повторно используя один и тот же экземпляр объекта через цикл foreach/for, и если давление GC вызывает проблемы с производительностью, я бы поднял его во внешний охват.
Ответ 3
Те и расточительны, и многословны.
foreach (var name in names)
{
Console.WriteLine("{0}1, {0}2, {0}3", name);
}
.
</tongueincheek>
Ответ 4
В зависимости от языка и компилятора это может быть или не быть одинаковым. Для С# я ожидаю, что полученный код будет очень похож.
Моя собственная философия в этом проста:
Оптимизация для простоты понимания.
Все остальное - преждевременная оптимизация! Крупнейшим узким местом в большинстве разработок является время и внимание разработчика. Если вы абсолютно должны выжать каждый последний цикл ЦП, то непременно сделайте это, но если у вас нет необходимости делать бизнес или писать критический компонент (общая библиотека, ядро операционной системы и т.д.), Вам лучше ждать, пока вы не может сравниться с готовой программой. В то время оптимизация нескольких из самых дорогостоящих процедур оправдана, до этого это почти наверняка пустая трата времени.
Ответ 5
Я не уверен, что вы получаете, определяя строковую переменную за пределами цикла. Строки неизменяемы, поэтому они не используются повторно. Всякий раз, когда вы назначаете им, создается новый экземпляр.
Ответ 6
Я думаю, это зависит от того, что вы пытаетесь решить. Мне нравится второй пример, потому что вы можете переместить код за один шаг. Мне нравится первый пример, потому что он быстрее из-за меньшего количества манипуляций со стеком, меньшей фрагментации памяти и создания/создания объектов меньше.
Ответ 7
Я обнаружил, что декларации "подъема" из циклов обычно являются лучшей долгосрочной стратегией обслуживания. Компилятор обычно будет разбирать вещи приемлемо для производительности.
Ответ 8
Оба они практически те же, что и производительность (строки неизменяемы), но что касается читаемости... Я бы сказал, что это не очень хорошо. Вы можете легко просто сделать все это в Console.WriteLine.
Возможно, вы можете опубликовать реальную проблему вместо примера?
Ответ 9
Я вообще объявляю переменные как можно ближе к их использованию, поскольку позволяет определить область охвата, которая в этом случае будет вашим вторым примером. Resharper имеет тенденцию поощрять этот стиль также.
Ответ 10
Для данных типа POD объявляется ближе всего к первому использованию. Для любого типа класса, который выполняет любое распределение памяти, вам следует рассмотреть возможность объявления этих элементов вне любых циклов. Строки почти наверняка будут выполнять некоторую форму распределения, и большинство реализаций (по крайней мере, на С++) попытаются повторно использовать память, если это возможно. Распределение на основе кучи может быть очень медленным.
Я когда-то профилировал немного кода на С++, который включал класс, который new'd данные в его ctor. С переменной, объявленной за пределами цикла, она выполнялась на 17% быстрее, чем с переменной, объявленной внутри цикла. YMMV в С#, так что производительность профиля вы можете быть очень удивлены результатами.
Ответ 11
Это моя любимая часть Linq, которая, как мне кажется, подходит здесь:
names.ForEach(x => Console.WriteLine("{0}1, {0}2, {0}3", x));
Ответ 12
Следуйте простому правилу при объявлении переменных
Объявите его, когда вам это нужно в первый раз