Как работают циклы foreach в С#?

Какие типы классов могут использовать циклы foreach?

Ответ 1

На самом деле, строго, все, что вам нужно использовать foreach, является общедоступным методом GetEnumerator(), который возвращает что-то с помощью метода bool MoveNext() и свойства ? Current {get;}. Однако самым общим значением является то, что реализует IEnumerable/IEnumerable<T>, возвращая IEnumerator/IEnumerator<T>.

Подразумевается, что это включает в себя все, что реализует ICollection/ICollection<T>, например, что-либо вроде Collection<T>, List<T>, массивы (T[]) и т.д. Таким образом, любой стандартный "сбор данных" будет вообще поддержка foreach.

Для доказательства первой точки следующее прекрасно работает:

using System;
class Foo {
    public int Current { get; private set; }
    private int step;
    public bool MoveNext() {
        if (step >= 5) return false;
        Current = step++;
        return true;
    }
}
class Bar {
    public Foo GetEnumerator() { return new Foo(); }
}
static class Program {
    static void Main() {
        Bar bar = new Bar();
        foreach (int item in bar) {
            Console.WriteLine(item);
        }
    }
}

Как это работает?

Цикл foreach, подобный foreach(int i in obj) {...} kinda, равен:

var tmp = obj.GetEnumerator();
int i; // up to C# 4.0
while(tmp.MoveNext()) {
    int i; // C# 5.0
    i = tmp.Current;
    {...} // your code
}

Однако существуют вариации. Например, он перечислитель (tmp) поддерживает IDisposable, он также используется (аналогично using).

Обратите внимание на разницу в размещении объявления "int i" внутри (С# 5.0) по сравнению с внешним (до С# 4.0) в цикле. Это важно, если вы используете i в анонимном методе/лямбда внутри вашего кодового блока. Но это еще одна история; -p

Ответ 2

От MSDN:

Оператор foreach повторяет группу встроенных операторов для каждого элемент в массив или объект Коллекция. Оператор foreachиспользуется для итерации по коллекции для получения желаемой информации, но не следует использовать для изменения содержимое коллекции, чтобы избежать непредсказуемые побочные эффекты. (акцент мой)

Итак, если у вас есть массив, вы можете использовать оператор foreach для итерации по массиву, например:

 int[] fibarray = new int[] { 0, 1, 2, 3, 5, 8, 13 };
    foreach (int i in fibarray)
    {
        System.Console.WriteLine(i);
    }

Вы также можете использовать его для итерации через коллекцию List<T>, например:

List<string> list = new List<string>();

foreach (string item in list)
{
    Console.WriteLine(item);
}

Ответ 3

Согласно сообщению блога Duck Notation, используется утиная печать.

Ответ 4

Здесь docs: Основная статья С массивами С объектами коллекции

Важно отметить, что "Тип элемента коллекции должен быть конвертирован в тип идентификатора". Это иногда невозможно проверить во время компиляции и может генерировать исключение во время выполнения, если тип экземпляра не присваивается ссылочному типу.

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

List<Fruit> fruitBasket = new List<Fruit>() { new Apple(), new Orange() };
foreach(Apple a in fruitBasket)

Это безопасно фильтрует список только в Яблоках, используя Enumerable.OfType

foreach(Apple a in fruitBasket.OfType<Apple>() )

Ответ 5

IList<ListItem> illi = new List<ListItem>();
ListItem li = null;

foreach (HroCategory value in listddlsubcategory)
{
    listddlsubcategoryext = server.getObjectListByColumn(typeof(HroCategory), "Parentid", value.Id);
    li = new ListItem();
    li.Text = value.Description;
    li.Value = value.Id.ToString();
    illi.Add(li);
    IList<ListItem> newilli = new List<ListItem>();
    newilli = SubCatagoryFunction(listddlsubcategoryext, "-->");
    foreach (ListItem c in newilli)
    {
        illi.Add(c);
    }
}