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