Оператор For Each...Next (Visual Basic)

Повторяет группу инструкций для каждого элемента в коллекции.

Синтаксис

For Each element [ As datatype ] In group
    [ statements ]
    [ Continue For ]
    [ statements ]
    [ Exit For ]
    [ statements ]
Next [ element ]

Детали

Термин Определение
element Обязательный в инструкции For Each . Необязательно в инструкции Next . Variable. Используется для итерации элементов коллекции.
datatype Необязательный параметр, если включен (по умолчанию) или element уже объявлен; требуется, если Option InferOption Infer отключен и element еще не объявлен. Тип данных element.
group Обязательный. Переменная с типом, который является типом коллекции или object. Ссылается на коллекцию, над которой statements следует повторяться.
statements Необязательно. Один или несколько операторов между For EachNext ими, выполняющимися на каждом элементе.group
Continue For Необязательно. Передает элемент управления в начало For Each цикла.
Exit For Необязательно. Передает элемент управления из For Each цикла.
Next Обязательный. Завершает определение For Each цикла.

Простой пример

For EachИспользуйте цикл ...Next для повторения набора инструкций для каждого элемента коллекции или массива.

Совет

А для... Следующая инструкция хорошо работает, когда можно связать каждую итерацию цикла с переменной элемента управления и определить начальные и конечные значения этой переменной. Однако при работе с коллекцией концепция начальных и конечных значений не имеет смысла, и вы не обязательно знаете, сколько элементов имеет коллекция. В этом случае For Eachцикл ...Next часто лучше выбрать.

В следующем примере For Each...Next оператор выполняет итерацию по всем элементам коллекции List.

' Create a list of strings by using a
' collection initializer.
Dim lst As New List(Of String) _
    From {"abc", "def", "ghi"}

' Iterate through the list.
For Each item As String In lst
    Debug.Write(item & " ")
Next
Debug.WriteLine("")
'Output: abc def ghi

Дополнительные примеры см. в разделе "Коллекции и массивы".

Вложенные циклы

Вы можете вложить For Each циклы, поместив один цикл в другой.

В следующем примере показан вложенный For Each...Next Структуры.

' Create lists of numbers and letters
' by using array initializers.
Dim numbers() As Integer = {1, 4, 7}
Dim letters() As String = {"a", "b", "c"}

' Iterate through the list by using nested loops.
For Each number As Integer In numbers
    For Each letter As String In letters
        Debug.Write(number.ToString & letter & " ")
    Next
Next
Debug.WriteLine("")
'Output: 1a 1b 1c 4a 4b 4c 7a 7b 7c

При вложенных циклах каждый цикл должен иметь уникальную element переменную.

Вы также можете вложить различные типы структур управления друг в друга. Дополнительные сведения см. в разделе "Вложенные структуры управления".

Выход и продолжение

Оператор Exit For приводит к выходу из оператора For...Next цикл и передача элемента управления инструкции, следующей за инструкцией Next .

Оператор Continue For передает элемент управления немедленно в следующую итерацию цикла. Дополнительные сведения см. в инструкции "Продолжить".

В следующем примере показано, как использовать Continue For инструкции и Exit For инструкции.

Dim numberSeq() As Integer =
    {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}

For Each number As Integer In numberSeq
    ' If number is between 5 and 8, continue
    ' with the next iteration.
    If number >= 5 And number <= 8 Then
        Continue For
    End If

    ' Display the number.
    Debug.Write(number.ToString & " ")

    ' If number is 10, exit the loop.
    If number = 10 Then
        Exit For
    End If
Next
Debug.WriteLine("")
' Output: 1 2 3 4 9 10

Можно поместить любое количество Exit For операторов в For Each цикл. При использовании в вложенных циклах Exit For выполнение приводит к выходу из самого внутреннего цикла и передаче управления на следующий более высокий уровень вложенностиFor Each.

Exit For часто используется после оценки некоторых условий, например, в If...Then...Else Структура. Для следующих условий может потребоваться использовать Exit For следующее:

  • Продолжение итерации является ненужным или невозможным. Это может быть вызвано ошибочным значением или запросом на завершение работы.

  • Исключение поймано в Try...Catch...Finally. Вы можете использовать Exit For в конце Finally блока.

  • Там бесконечный цикл, который является циклом, который может запускать большое или даже бесконечное количество раз. При обнаружении такого условия можно использовать Exit For для escape-цикла. Дополнительные сведения см. в разделе "Do... Оператор цикла.

Итераторы

Для выполнения пользовательской итерации по коллекции используется итератор . Итератор может быть функцией или методом Get доступа. Он использует инструкцию Yield для возврата каждого элемента коллекции по одному за раз.

Вы вызываете итератор с помощью инструкции For Each...Next . Каждая итерация цикла For Each вызывает итератор. Yield Когда оператор достигается в итераторе, возвращается выражение в Yield инструкции, а текущее расположение в коде сохраняется. При следующем вызове итератора выполнение возобновляется с этого места.

В следующем примере используется функция итератора. Функция итератора имеет Yield оператор, который находится внутри for... Следующий цикл. В методе ListEvenNumbers каждая итерация For Each текста инструкции создает вызов функции итератора, которая переходит к следующей Yield инструкции.

Public Sub ListEvenNumbers()
    For Each number As Integer In EvenSequence(5, 18)
        Debug.Write(number & " ")
    Next
    Debug.WriteLine("")
    ' Output: 6 8 10 12 14 16 18
End Sub

Private Iterator Function EvenSequence(
ByVal firstNumber As Integer, ByVal lastNumber As Integer) _
As System.Collections.Generic.IEnumerable(Of Integer)

    ' Yield even numbers in the range.
    For number = firstNumber To lastNumber
        If number Mod 2 = 0 Then
            Yield number
        End If
    Next
End Function

Дополнительные сведения см. в разделе "Итераторы", "Оператор доходности" и "Итератор".

Техническая реализация

For EachКогда ...Next выполнение инструкций, Visual Basic оценивает коллекцию только один раз перед началом цикла. Если оператор блокирует изменения element или groupэти изменения не влияют на итерацию цикла.

Когда все элементы коллекции были последовательно назначены element, For Each цикл останавливается и элемент управления передается инструкции, приведенной ниже инструкции Next .

Если параметр "Вывод параметров" включен (его параметр по умолчанию), компилятор Visual Basic может определить тип elementданных. Если он отключен и element не объявлен за пределами цикла, необходимо объявить его в инструкции For Each . Чтобы объявить тип element данных явно, используйте As предложение. Если тип данных элемента не определен вне For Eachконструкции ...Next, его область является телом цикла. Обратите внимание, что нельзя объявлять element как вне, так и внутри цикла.

При необходимости можно указать element в инструкции Next . Это повышает удобочитаемость программы, особенно если у вас есть вложенные For Each циклы. Необходимо указать ту же переменную, что и в соответствующей For Each инструкции.

Может потребоваться избежать изменения значения element внутри цикла. Это может сделать его более сложным для чтения и отладки кода. Изменение значения group не влияет на коллекцию или его элементы, которые были определены при первом вводе цикла.

При вложении циклов, если Next оператор внешнего уровня вложения обнаруживается до Next внутреннего уровня, компилятор сигнализирует об ошибке. Однако компилятор может обнаружить эту перекрывающиеся ошибки только в том случае, если указана element в каждой Next инструкции.

Если код зависит от обхода коллекции в определенном порядке, For Eachцикл ...Next не является лучшим выбором, если вы не знаете характеристики объекта перечислителя, предоставляемые коллекцией. Порядок обхода не определяется Visual Basic, а MoveNext методом объекта перечислителя. Таким образом, вы можете не спрогнозировать, в какой элемент коллекции будет возвращен elementпервый объект, или который будет возвращен после заданного элемента. Вы можете добиться более надежных результатов, используя другую структуру цикла, например For...Next или Do...Loop.

Среда выполнения должна быть в состоянии преобразовать элементы в groupelement. Оператор [Option Strict] определяет, разрешены ли как расширенные, так и сужающие преобразования (Option Strict отключены, значение по умолчанию) или разрешены ли только расширенные преобразования (Option Strict включены). Дополнительные сведения см. в разделе "Сужение преобразований".

Тип group данных должен быть ссылочным типом, который ссылается на коллекцию или массив, который можно перечислить. Чаще всего это означает, что group объект, реализующий IEnumerable интерфейс System.Collections пространства имен или IEnumerable<T> интерфейс System.Collections.Generic пространства имен. System.Collections.IEnumerable определяет GetEnumerator метод, который возвращает объект перечислителя для коллекции. Объект перечислителя реализует System.Collections.IEnumerator интерфейс System.Collections пространства имен и предоставляет Current свойство и ResetMoveNext методы. Visual Basic использует их для обхода коллекции.

Сужающие преобразования

Если Option Strict задано значение On, сужающие преобразования обычно вызывают ошибки компилятора. For Each Однако в инструкции преобразования элементов, которые будут groupelement оцениваться и выполняться во время выполнения, и ошибки компилятора, вызванные сужающими преобразованиями, подавляются.

В следующем примере назначение m в качестве начального значения n не компилируется, Option Strict так как преобразование объекта Long в сужающее Integer преобразование является сужающим. Однако в инструкции не сообщается об ошибке For Each компилятора, несмотря на то, что назначение требует number того же преобразования из IntegerLong . For Each В инструкции, содержащей большое число, возникает ошибка во время выполнения при ToInteger применении к большому числу.

Option Strict On

Imports System

Module Program
    Sub Main(args As String())
        ' The assignment of m to n causes a compiler error when 
        ' Option Strict is on.
        Dim m As Long = 987
        'Dim n As Integer = m

        ' The For Each loop requires the same conversion but
        ' causes no errors, even when Option Strict is on.
        For Each number As Integer In New Long() {45, 3, 987}
            Console.Write(number & " ")
        Next
        Console.WriteLine()
        ' Output: 45 3 987

        ' Here a run-time error is raised because 9876543210
        ' is too large for type Integer.
        'For Each number As Integer In New Long() {45, 3, 9876543210}
        '    Console.Write(number & " ")
        'Next
    End Sub
End Module

Вызовы IEnumerator

При запуске For Eachцикла ...Next Visual Basic проверяет, group что относится к допустимому объекту коллекции. Если нет, он создает исключение. В противном случае вызывает MoveNext метод и Current свойство объекта перечислителя, чтобы вернуть первый элемент. Если MoveNext указывает, что следующий элемент отсутствует, то есть если коллекция пуста, For Each цикл останавливается и элемент управления передается в инструкцию после инструкции Next . В противном случае Visual Basic задает element первый элемент и запускает блок инструкций.

Каждый раз, когда Visual Basic сталкивается с оператором Next , он возвращается в инструкцию For Each . Снова он вызывает MoveNext и Current возвращает следующий элемент, а затем снова запускает блок или останавливает цикл в зависимости от результата. Этот процесс продолжается до тех пор, пока не MoveNext будет указано, что отсутствует следующий элемент или Exit For оператор.

Изменение коллекции. Объект перечислителя, возвращаемый GetEnumerator обычно, не позволяет изменять коллекцию, добавляя, удаляя, заменяя или переупорядочения элементов. Если после инициирования For Eachцикла ...Next , объект перечислителя становится недействительным, а следующая попытка доступа к элементу вызывает InvalidOperationException исключение.

Однако эта блокировка изменений не определяется Visual Basic, а реализацией IEnumerable интерфейса. Можно реализовать IEnumerable таким образом, чтобы можно было изменять во время итерации. Если вы рассматриваете такие динамические изменения, убедитесь, что вы понимаете характеристики IEnumerable реализации в используемой коллекции.

Изменение элементов коллекции. Свойство Current объекта перечислителя — ReadOnly, и возвращает локальную копию каждого элемента коллекции. Это означает, что нельзя изменять сами элементы в цикле For Each...Next . Любые изменения, внесенные вами, влияют только на локальную копию Current и не отражаются обратно в базовую коллекцию. Однако если элемент является ссылочным типом, можно изменить элементы экземпляра, на который он указывает. В следующем примере изменяется BackColor элемент каждого thisControl элемента. Однако вы не можете изменить thisControl себя.

Sub LightBlueBackground(thisForm As System.Windows.Forms.Form)
    For Each thisControl In thisForm.Controls
        thisControl.BackColor = System.Drawing.Color.LightBlue
    Next thisControl
End Sub

Предыдущий пример может изменять BackColor элемент каждого thisControl элемента, хотя он не может изменить thisControl себя.

Обход массивов. Array Так как класс реализует IEnumerable интерфейс, все массивы предоставляют GetEnumerator метод. Это означает, что можно выполнять итерацию по массиву с циклом For Each...Next . Однако можно считывать только элементы массива. Вы не можете изменить их.

Пример 1

В следующем примере перечислены все папки в каталоге C:\ с помощью DirectoryInfo класса.

Dim dInfo As New System.IO.DirectoryInfo("c:\")
For Each dir As System.IO.DirectoryInfo In dInfo.GetDirectories()
    Debug.WriteLine(dir.Name)
Next

Пример 2

Приведенный ниже пример демонстрирует процедуру сортировки коллекции. В примере сортируются экземпляры Car класса, хранящегося в объекте List<T>. Класс Car реализует интерфейс IComparable<T>, который требует реализации метода CompareTo.

Каждый вызов CompareTo метода выполняет одно сравнение, используемое для сортировки. Написанный пользователем код в методе CompareTo возвращает значение для каждого сравнения текущего объекта с другим объектом. Возвращаемое значение меньше нуля, если текущий объект меньше другого объекта, больше нуля, если текущий объект больше другого объекта, и равняется нулю, если объекты равны. Это позволяет определить в коде условия для отношения «больше», «меньше» и «равно».

В методе ListCars оператор cars.Sort() сортирует список. Этот вызов метода SortList<T> приводит к тому, что метод CompareTo вызывается автоматически для объектов Car в List.

Public Sub ListCars()

    ' Create some new cars.
    Dim cars As New List(Of Car) From
    {
        New Car With {.Name = "car1", .Color = "blue", .Speed = 20},
        New Car With {.Name = "car2", .Color = "red", .Speed = 50},
        New Car With {.Name = "car3", .Color = "green", .Speed = 10},
        New Car With {.Name = "car4", .Color = "blue", .Speed = 50},
        New Car With {.Name = "car5", .Color = "blue", .Speed = 30},
        New Car With {.Name = "car6", .Color = "red", .Speed = 60},
        New Car With {.Name = "car7", .Color = "green", .Speed = 50}
    }

    ' Sort the cars by color alphabetically, and then by speed
    ' in descending order.
    cars.Sort()

    ' View all of the cars.
    For Each thisCar As Car In cars
        Debug.Write(thisCar.Color.PadRight(5) & " ")
        Debug.Write(thisCar.Speed.ToString & " ")
        Debug.Write(thisCar.Name)
        Debug.WriteLine("")
    Next

    ' Output:
    '  blue  50 car4
    '  blue  30 car5
    '  blue  20 car1
    '  green 50 car7
    '  green 10 car3
    '  red   60 car6
    '  red   50 car2
End Sub

Public Class Car
    Implements IComparable(Of Car)

    Public Property Name As String
    Public Property Speed As Integer
    Public Property Color As String

    Public Function CompareTo(ByVal other As Car) As Integer _
        Implements System.IComparable(Of Car).CompareTo
        ' A call to this method makes a single comparison that is
        ' used for sorting.

        ' Determine the relative order of the objects being compared.
        ' Sort by color alphabetically, and then by speed in
        ' descending order.

        ' Compare the colors.
        Dim compare As Integer
        compare = String.Compare(Me.Color, other.Color, True)

        ' If the colors are the same, compare the speeds.
        If compare = 0 Then
            compare = Me.Speed.CompareTo(other.Speed)

            ' Use descending order for speed.
            compare = -compare
        End If

        Return compare
    End Function
End Class

См. также