Разница между foreach и циклами над классом IEnumerable в С#

Мне сказали, что есть разница в производительности между следующими кодовыми блоками.

foreach (Entity e in entityList)
{
 ....
}

и

for (int i=0; i<entityList.Count; i++)
{
   Entity e = (Entity)entityList[i];
   ...
}

где

List<Entity> entityList;

Я не ожидаю CLR, но из того, что я могу сказать, они должны сводиться к одному и тому же коду. Есть ли у кого-нибудь конкретное (черт, я бы взял упакованную грязь) доказательства так или иначе?

Ответ 1

foreach создает экземпляр счетчика (возвращается из GetEnumerator), и этот счетчик также сохраняет состояние в течение цикла foreach. Затем он повторно вызывает объект Next() в перечислении и запускает ваш код для каждого возвращаемого им объекта.

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

Ответ 2

Здесь - хорошая статья, в которой показаны различия между двумя циклами IL.

Foreach технически медленнее, но намного проще в использовании и легче читать. Если производительность критическая, я предпочитаю цикл foreach в цикле for.

Ответ 3

Образец foreach примерно соответствует этому коду:

using(IEnumerator<Entity> e = entityList.GetEnumerator()) {
    while(e.MoveNext()) {
        Entity entity = e.Current;
        ...
    }
}

Здесь существует две издержки: регулярный цикл цикла не должен оплачиваться:

  • Стоимость выделения объекта перечисления по объекту entityList.GetEnumerator().
  • Стоимость двух виртуальных методов вызывает (MoveNext и Current) для каждого элемента списка.

Ответ 4

Один пункт пропустил здесь: Список имеет свойство Count, он внутренне отслеживает, сколько элементов в нем.

IEnumerable НЕ ДЕЛАЕТ.

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

Спорный вопрос, хотя, поскольку в IEnumerable вы не можете ссылаться на элементы по индексу.

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

Если вы хотите, чтобы гибкость использовала foreach и программу для IEnumerable. (позволяя использовать linq и/или доходность возврата).

Ответ 5

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

Ответ 6

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

const int ArraySize = 10;
int[] values = new int[ArraySize];

//...

for (int i = 0; i 

In this case, depending on the complexity of the loop body, the compiler might be able to replace the loop with inline calls. I have no idea if the .NET compiler does this, and it of limited utility if the size of the enumerable type is dynamic.

One situation where foreach might perform better is with data structures like a linked list where random access means traversing the list; the enumerator used by foreach will probably iterate one item at a time, making each access O(1) and the full loop O(n), but calling the indexer means starting at the head and finding the item at the right index; O(N) each loop for O(n^2).

Personally I don't usually worry about it and use foreach any time I need all items and don't care about the index of the item. If I'm not working with all of the items or I really need to know the index, I use for. The only time I could see it being a big concern is with structures like linked lists.

Ответ 7

For Loop
for loop is used to perform the opreration n times
for(int i=0;i<n;i++)
{
l=i;
}
foreach loop

int[] i={1,2,3,4,5,6}
foreach loop is used to perform each operation value/object in IEnumarable 
foreach(var k in i)
{
l=k;
}