Подтвердить что ты не робот

В .NET, используя "foreach" для итерации экземпляра IEnumerable <ValueType> создаст копию? Так я должен предпочесть использовать "for" вместо "foreach"?

В .NET, используя "foreach" для итерации, экземпляр IEnumerable создаст копию? Так что я должен предпочесть использовать "for" вместо "foreach" ?

Я написал код для подтверждения:

struct ValueTypeWithOneField
{
    private Int64 field1;
}

struct ValueTypeWithFiveField
{
    private Int64 field1;
    private Int64 field2;
    private Int64 field3;
    private Int64 field4;
    private Int64 field5;
}

public class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("one field");
        Test<ValueTypeWithOneField>();

        Console.WriteLine("-----------");

        Console.WriteLine("Five field");
        Test<ValueTypeWithFiveField>();

        Console.ReadLine();
    }

    static void Test<T>()
    {
        var test = new List<T>();
        for (int i = 0; i < 5000000; i++)
        {
            test.Add(default(T));
        }

        Stopwatch sw = new Stopwatch();

        for (int i = 0; i < 5; i++)
        {
            sw.Start();

            foreach (var item in test)
            {

            }

            sw.Stop();
            Console.WriteLine("foreach " + sw.ElapsedMilliseconds);
            sw.Restart();

            for (int j = 0; j < test.Count; j++)
            {
                T temp = test[j];
            }

            sw.Stop();
            Console.WriteLine("for " + sw.ElapsedMilliseconds);
            sw.Reset();
        }
    }}

И это результат, который я получил после запуска кода:

    one field
    foreach 68
    for 72
    foreach 68
    for 72
    foreach 67
    for 72
    foreach 64
    for 73
    foreach 68
    for 72
    -----------
    Five field
    foreach 272
    for 193
    foreach 273
    for 191
    foreach 272
    for 190
    foreach 271
    for 190
    foreach 275
    for 188

Как мы видим в результате, "foreach" всегда занимает больше времени, чем "для".

Так что я предпочитаю использовать вместо "foreach" вместо "foreach" при повторении через общий набор типов значений?

Примечание: спасибо за напоминание, я редактировал код и результат. но все же, foreach работает медленнее, чем для.

4b9b3361

Ответ 1

Ваш тест не является точным; в версии foreach вы фактически разворачиваете перечислитель и извлекаете каждое значение из списка (даже если вы его не используете). В версии for вы вообще ничего не делаете со списком, кроме поиска его свойства Count. Вы, по сути, проверяете производительность перечислителя, проходящего через коллекцию, по сравнению с добавлением целочисленной переменной в эквивалентное количество раз.

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

Таким образом, ответ на ваш вопрос - да. Копия значения будет создана с каждым присваиванием или return.

Производительность

Этот прорыв псевдокода должен объяснить, почему foreach несколько медленнее, чем использование for в этом конкретном случае:

foreach:

try
{
    var en = test.GetEnumerator(); //creates a ListEnumerator
    T item;

    while(en.MoveNext()) // MoveNext increments the current index and returns
                         // true if the new index is valid, or false if it's
                         // beyond the end of the list. If it returns true,
                         // it retrieves the value at that index and holds it 
                         // in an instance variable
    {
        item = en.Current; // Current retrieves the value of the current instance
                           // variable
    }
}
finally { }

for:

int index = -1;
T item;

while(++index < test.Count)
{
    item = test[index];
}

Как вы можете видеть, в реализации for просто меньше кода, а foreach - слой абстракции (перечислитель) поверх for. Я написал for, используя цикл while, чтобы показать две версии в аналогичном представлении.

При всем том, что сказано...

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

Ответ 2

Вопрос ваш, слишком сложный. Разбейте его.

Использует ли "foreach" для итерации последовательность типов значений, создает копию последовательности?

Нет.

Использует ли "foreach" для итерации последовательность типов значений, создает копию каждого значения?

Да.

Использует ли "for" эквивалентную итерацию индексированной последовательности типов значений, создает копию каждого значения?

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

Делает ли что-либо значение для типа значения, копирует значение?

Просто. Типы значений копируются по значению. Вот почему они называются типами значений. Единственное, что вы делаете, чтобы оценить типы, которые не делают копию, - это вызовы методам типа значения и передача переменной типа значения с помощью "out" или "ref". Типы значений копируются постоянно; почему типы значений часто медленнее ссылочных типов.

Использует ли "foreach" или "for" для итерации последовательность ссылочного типа копировать ссылку?

Да. Значение выражения ссылочного типа является ссылкой. Эта ссылка копируется всякий раз, когда она используется.

Итак, какова разница между типами значений и ссылочными типами в отношении их поведения при копировании?

Типы значений копируются по значению. Справочные типы копируют ссылку, но не относятся к ней. 16-байтовый тип значения копирует 16 байтов каждый раз, когда вы его используете. 16-байтовый ссылочный тип копирует ссылку 4 (или 8) байта каждый раз, когда вы ее используете.

Является ли цикл foreach медленнее, чем цикл for?

Часто это так. Цикл foreach часто делает больше работы, поскольку он создает перечислитель и вызывающие методы в перечислителе вместо того, чтобы просто увеличивать целое число. Целочисленные приращения очень быстрые. Также не забывайте, что перечислитель в цикле foreach должен быть удален, и это может занять время.

Должен ли я использовать цикл for вместо цикла foreach, потому что цикл for иногда достигает нескольких микросекунд быстрее?

Нет. Это глупо. Вы должны принимать интеллектуальные инженерные решения на основе эмпирических данных, ориентированных на клиента. Дополнительное бремя петли foreach крошечное. Клиент, вероятно, никогда не заметит. Что вам нужно сделать:

  • Задайте цели производительности на основе ввода клиента
  • Мера, чтобы увидеть, достигли ли вы своих целей.
  • Если вы этого не сделали, найдите самую медленную вещь, используя профайлер
  • Исправить
  • Повторяйте, пока не достигнете своих целей.

Коэффициенты очень хороши, что если у вас проблема с производительностью, изменение цикла foreach в цикле for не будет иметь никакого значения для вашей проблемы. Напишите код так, как он выглядит ясным и понятным сначала.

Ответ 3

Вы не перезагружаете "секундомер" после теста "для" , поэтому время, затраченное на тест "для", добавляется к следующему тесту "foreach". Кроме того, как правильно указано, вы должны выполнить назначение внутри "for", чтобы имитировать точное поведение foreach.

sw.Start();

foreach (var item in test)
{

}

sw.Stop();
Console.WriteLine("foreach " + sw.ElapsedMilliseconds);
sw.Restart();

for (int j = 0; j < test.Count; j++)
{
    T temp = test[j];
}

sw.Stop();
Console.WriteLine("for " + sw.ElapsedMilliseconds);
sw.Reset(); // -- This bit is missing!

Ответ 4

В вашем цикле for я не вижу, что вы действительно обращаетесь к элементам из test. Если вы добавите var x = test[i]; в цикл for, вы увидите, что производительность будет (практически) одинаковой.

Каждый доступ к свойству типа значения создает копию, либо с помощью foreach, либо используя indexer в list в цикле for.

Ответ 6

Ваш тест несправедлив. Рассмотрим, как работает цикл foreach. У вас есть следующий код:

foreach (var item in test)
{

}

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

Затем у вас есть этот код:

for (int j = 0; j < test.Count; j++)
{

}

Это вовсе не касается основной коллекции. Он не читает и не назначает переменную на каждой итерации. Он просто увеличивает число целых test.Count раз, поэтому, конечно, это быстрее. И если компилятор умный, он увидит, что в цикле не происходит никакой операции и просто оптимизируйте все это.

Справедливое сравнение заменит этот второй бит кода чем-то вроде:

var item;
for (int j = 0; j < test.Count; j++)
{
    item = test.get(j);
} 

Это более похоже на то, что делает цикл foreach.

Что касается использования, это действительно вопрос личного предпочтения и стиля кодирования. Обычно я чувствую, что foreach более понятен, чем for(...) с точки зрения удобочитаемости.

Ответ 7

Я думаю, что foreach предоставляет абстрактный способ циклирования, но он технически медленнее, чем цикл for, хорошая статья о различиях между циклами for и foreach может быть найдена здесь

Ответ 8

Я нашел только один случай, когда это имеет значение. Разработка для Windows Phone 7. Есть две причины, по которым нужно изменить

foreach(var item in colletion)
{
}

Для

int length = array.Length;
for(int i = 0; i < length; ++i)
{
}

в игре XNA, если коллекции большие или часто вызываются (например, метод Update).

  • Это немного быстрее
  • меньше мусора

и мусор имеет решающее значение, поскольку Compact Framework GC запускает каждое распределение 1 МБ, в результате это может вызвать раздражающие зависания.