Можно ли начать итерацию с элемента, отличного от первого, с помощью foreach?

87

Я думаю о реализации IEnumerable для моей пользовательской коллекции (дерева), чтобы я мог использовать foreach для обхода моего дерева. Однако, насколько мне известно, foreach всегда начинается с первого элемента коллекции. Я хотел бы выбрать, с какого элемента запускается foreach. Можно ли как-то изменить элемент, с которого начинается foreach?

ад
источник
1
Я бы следовал принципу наименьшего удивления: en.wikipedia.org/wiki/Principle_of_least_astonishment
ChaosPandion
Если вы не начинаете с первого элемента, как вы определяете поведение итератора? Что произойдет, когда он достигнет конца последовательности? Затем он возвращается к первому элементу и перебирает оставшиеся элементы?
Dan J
Комментарий @ ChaosPandion - это то, к чему я собирался с этими вопросами. :)
Dan J

Ответы:

163

Да. Сделайте следующее:

Collection<string> myCollection = new Collection<string>;

foreach (string curString in myCollection.Skip(3))
    //Dostuff

Skip- это функция IEnumerable, которая пропускает любое указанное вами количество, начиная с текущего индекса. С другой стороны, если вы хотите использовать только первые три, вы должны использовать .Take:

foreach (string curString in myCollection.Take(3))

Их можно даже сочетать вместе, поэтому, если вам нужны только 4-6 элементов, вы можете:

foreach (string curString in myCollection.Skip(3).Take(3))
MoarCodePlz
источник
Хороший звонок. Автозаполнение WTB (не могу вспомнить его настоящее имя) в stackoverflow;)
MoarCodePlz
Это действительно пропускает или просто создает копию? IE, эти двое были бы разными: foreach(List<string> row in rows.skip(1)){ row[0] = "newInfo"; } vs for(int i=1; i<rows.Count;i++){ List<string> row = rows[i]; row[0] = "newStuff"; }
TigerBear
25

Для этого проще всего использовать Skipметод LINQ to Objects, чтобы пропустить заданное количество элементов:

foreach (var value in sequence.Skip(1)) // Skips just one value
{
    ...
}

Очевидно, просто измените 1 на любое другое значение, чтобы пропустить другое количество элементов ...

Точно так же вы можете использовать Takeдля ограничения количества возвращаемых элементов.

Вы можете прочитать больше об обоих из них (и связанный с ним SkipWhileи TakeWhileметоды) в моем блоге Edulinq серии .

Джон Скит
источник
Джон, не возникает ли риск исключения, если "последовательность" равна нулю?
WEFX
@WEFX: Ну, мы понятия не имеем, sequenceзаконно это может быть nullили нет. Предположительно ОП может справиться с этим самостоятельно - на самом деле это не часть вопроса ...
Джон Скит
5

Вы можете использовать Enumerable.Skip, чтобы пропустить некоторые элементы и начать с них.

Например:

foreach(item in theTree.Skip(9))  // Skips the first 9 items
{
    // Do something

Однако, если вы пишете дерево, вы можете захотеть предоставить член в самом элементе дерева, который будет возвращать новый, IEnumerable<T>который будет перечисляться оттуда вниз. Это потенциально может быть более полезным в долгосрочной перспективе.

Рид Копси
источник
Что-то вроде IEnumerable<T> SubTree(rootIndex)? Хороший звонок!
Dan J
Спасибо за ответ, skip выглядит многообещающе, читая информацию MSDN о skip, я пришел к выводу, что, возможно, можно написать метод StartFrom (это дерево IEnumerable <MyTree>, элемент MyTreeElement), который был бы методом расширения для IEnumerable <MyTree>, который вернет IEnumerable <MyTreeElement>, с которого я должен начать итерацию. Тогда, возможно, я мог бы использовать этот метод как foreach (элемент var в tree.StartFrom (someElement))).
inferno
@inferno: Да, вы могли бы легко это сделать. Однако я бы просто добавил в MyTreeElement метод / свойство, которое было бы больше похоже на:, IEnumerable<MyTreeElement> Childrenчтобы вы могли просто сделать: foreach(var item in someElement.Children)- гораздо более понятным, чем использование методов расширения IMO.
Рид Копси
-1

Foreachбудет перебирать вашу коллекцию способом, определенным вашей реализацией IEnumerable. Итак, хотя вы можете пропускать элементы (как было предложено выше), вы по-прежнему технически повторяете элементы в том же порядке.

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

Роб
источник
-1

Если вы хотите пропустить чтение строк в DataGridView, попробуйте это

foreach (DataGridViewRow row in dataGridView1.Rows.Cast<DataGridViewRow().Skip(3))

Если вы хотите скопировать содержимое одной DataGridViewстроки в другую, пропуская строки, попробуйте следующее:

foreach (DataGridViewRow row in dataGridView1.Rows.Cast<DataGridViewRow>().Skip(3))
{
    foreach (DataGridViewCell cell in row.Cells)
    {
        string value = cell.Value.ToString();
        dataGridView2.Rows[i].Cells[j].Value = cell.Value.ToString();
        j++;
    }
    i++;
    j = 0;
}

это копирует содержимое из одного DataGridViewв другой, пропуская 3 строки.

Джунед Хан Момин
источник
Это не отвечает на заданный вопрос - вопрос вообще не упоминается DataGridView(или даже о структуре пользовательского интерфейса).
NightOwl888
@ NightOwl888 на это был дан ответ, потому что вопрос заключался в пропуске некоторых итераций и повторении после этого в цикле foreach. В моем примере я просто использовал Skip () для применения к DataGridView.
Juned Khan Momin