← Все вопросы

Как работает foreach в C# и чем отличается от for?

Задан 7 дней назад342 просмотров2 ответа
6

Я раньше писал только на for через индекс, а тут в проекте почти везде foreach. Понимаю, что он перебирает элементы, но не до конца ясно, что под капотом и когда for всё-таки нужен. И ещё словил исключение, когда пытался удалять элементы из списка прямо внутри foreach — почему так нельзя?

2 ответа

9
✓ Принятый ответ — помог автору

foreach создан для одной задачи — пройтись по всем элементам коллекции по порядку, не думая об индексах.

var fruits = new List<string> { "яблоко", "груша", "слива" };
foreach (var fruit in fruits)
{
    Console.WriteLine(fruit);
}

Что под капотом. foreach работает с любым типом, реализующим IEnumerable (массивы, List<T>, Dictionary<,> и т.д.). Компилятор разворачивает его примерно в это:

var e = fruits.GetEnumerator();
while (e.MoveNext())
{
    var fruit = e.Current;
    Console.WriteLine(fruit);
}

То есть он берёт перечислитель и идёт по нему до конца. Именно поэтому индекс тебе недоступен — модель «дай следующий», а не «дай элемент номер i».

Почему нельзя менять коллекцию внутри foreach. Перечислитель запоминает «версию» коллекции. Как только ты внутри цикла делаешь fruits.Add(...) или fruits.Remove(...), версия меняется, и на следующем MoveNext() летит InvalidOperationException: Collection was modified. Это защита от неопределённого поведения. Решения: собирай элементы на удаление в отдельный список и удаляй после цикла, либо иди обычным for в обратную сторону:

for (int i = fruits.Count - 1; i >= 0; i--)
    if (fruits[i].StartsWith("я")) fruits.RemoveAt(i);

Когда брать for, а когда foreach:

  • Нужен индекс (печатать «1. ...», идти с шагом 2, менять элементы по позиции) — for.
  • Просто прочитать каждый элемент по очереди — foreach, он короче и без ошибок с границами.

Про var в foreach: пиши смело foreach (var x in items) — тип выводится автоматически из коллекции, это идиоматично и читаемо.

3

Ещё нюанс: переменная цикла в foreach доступна только для чтения. Нельзя написать foreach (var x in list) x = ...; — не скомпилируется. И если коллекция — это массив структур, изменение x всё равно не повлияло бы на оригинал, потому что foreach отдаёт копию значения. Для изменения элементов на месте нужен for с обращением по индексу.

Ваш ответ

Войдите, чтобы ответить на вопрос.
Поддержать проект