Як працюють цикли foreach у C #? [зачинено]


86

Які типи класів можуть використовувати foreachцикли?


5
Можливо, ви захочете вибрати іншу прийняту відповідь, оскільки обрана вами насправді не відповідає правильній відповіді.
Джордж Стокер,

Відповіді:


162

Насправді, строго кажучи, все, що вам потрібно використовувати foreach, це загальнодоступний GetEnumerator()метод, який повертає щось із bool MoveNext()методом та ? Current {get;}властивістю. Однак найпоширенішим значенням цього є "те, що реалізує IEnumerable/ IEnumerable<T>, повертаючи an 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) {...}свого роду прирівнюється до:

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" всередині (C # 5.0) порівняно з зовнішнім (вгору C # 4.0) циклом. Важливо, якщо ви використовуєте iанонімний метод / лямбда всередині кодового блоку. Але це вже інша історія ;-с


3
+1 за заглиблення. Зазвичай я не заглиблююся в питання, яке може бути питанням для початківців, оскільки новому програмісту це здається надзвичайним.
Джордж Стокер

Правда, Горток - отже, я продовжив роботу зі списками / масивами / тощо.
Марк Гравелл

Було б непогано згадати неявний привід виконання до змінної циклу - це може призвести до винятків несумісності типу.
Даніель Ервікер

3
Не забувайте: при використанні масиву з foreach компілятор створює простий for-loop(це можна побачити при роботі з IL).
Фелікс К.

1
@Marc Gravell: Добре, класно! Я відредагував допис, щоб зробити його більш зрозумілим - принаймні мені. Адже розміщення не тільки важливо в C # 5.0, це завжди важливо, лише те, що воно змінилося. Сподіваюся, ви не проти.
Пол Гроук,

7

З 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);
}

4
як не дивно, за даними MSDN ( msdn.microsoft.com/en-us/library/9yb8xew9(VS.80).aspx ), типи об’єктів не потребують реалізації IEnumerable. Буде працювати будь-який тип, який правильно визначає GetEnumerator, MoveNext, Reset і Current. Дивно, так?
Шон Рейлі

Охайний. Я цього не знав. :-)
Джордж Стокер


2

Ось документи: Основна стаття З масивами з об’єктами колекції

Важливо зазначити, що "Тип елемента колекції повинен бути конвертованим до типу ідентифікатора". Це іноді неможливо перевірити під час компіляції і може генерувати виняток виконання, якщо тип екземпляра не може бути призначений для посилального типу.

Це призведе до винятку під час роботи, якщо у кошику з фруктами є не Apple, наприклад апельсин.

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

Це безпечно фільтрує список лише до яблук за допомогою Enumerable.OfType

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

-1
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);
    }
}

-1

Корисну інформацію щодо цього питання також можна знайти на MSDN . Беручи суть з цієї статті:

Ключове слово foreach перераховує колекцію, виконуючи вбудований оператор один раз для кожного елемента в колекції:

foreach (var item in collection)
{
    Console.WriteLine(item.ToString());
}

Компілятор переводить цикл foreach, показаний у наведеному вище прикладі, у щось подібне до цієї конструкції:

IEnumerator<int> enumerator = collection.GetEnumerator();
while (enumerator.MoveNext())
{
    var item = enumerator.Current;
    Console.WriteLine(item.ToString());
}

-2

Ви можете спробувати це ...

List<int> numbers = new List<int>();
        numbers.Add(5);
        numbers.Add(15);
        numbers.Add(25);
        numbers.Add(35);

        Console.WriteLine("You are added total number: {0}",numbers.Count);
        foreach (int number in numbers)
        {
            Console.WriteLine("Your adding Number are: {0}", number);
        }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.