Оператор 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. Переменная. Используется для циклического прохода (итерации) элементов коллекции.

datatype

Является обязательным, если element еще не объявлен. Тип данных element.

group

Обязательное. Переменную с типом, тип коллекции или объект. Указывает на коллекцию, применительно к элементам которой будут повторяться операторы statements.

statements

Необязательный параметр. Один или несколько операторов, стоящих между операторами For Each и Next и выполняющихся применительно к каждому элементу group.

Continue For

Необязательный параметр. Передача управления в начало цикла For Each.

Exit For

Необязательный параметр. Передача управления из цикла For Each.

Next

Обязательное. Завершение определения цикла For Each.

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

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

Совет

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

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

' 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

Дополнительные примеры см. в разделах Коллекции (C# и Visual Basic) и Массивы в Visual Basic.

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

Циклы 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.

Также можно вложить друг в друга различные виды управляющих структур. Для получения дополнительной информации см. Вложенные структуры управления (Visual Basic).

Оставьте для; для

Выполнение причин выписки Оставьте для, чтобы отключить цикл For…Next и управление передач на инструкцию, которая следует за выписка Next.

Оператор Continue For передает управление непосредственно следующей итерации цикла. Для получения дополнительной информации см. Оператор Continue (Visual Basic).

В следующем примере показано, как используются операторы 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 7, 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. При использовании вложенных циклов For Each оператор Exit For вызывает выход из самого внутреннего цикла и передает управление следующему уровню вложения.

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

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

  • Исключение перехватывается в Try...Catch...Finally. Можно использовать Exit For в конце блока Finally.

  • Там бесконечный цикл, то есть цикл, которые может быть запущен большое или даже бесконечное количество раз. При обнаружении таких условий для выхода из цикла можно использовать Exit For. Дополнительные сведения см. в разделе Оператор Do...Loop (Visual Basic).

Итераторы

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

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

В следующем примере используется функция итератора. Функция итератора имеет оператора Yield, внутри цикла For… Next. В методе 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

Дополнительные сведения см. в разделах Итераторы (C# и Visual Basic), Оператор Yield (Visual Basic) и Итератор (Visual Basic).

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

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

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

Если element не было объявлено вне этого цикла, необходимо объявить его в инструкцию For Each. Можно объявить тип element явным образом при помощи оператора As, или можно использовать определение типа для назначения типа. В этом случае областью действия element является основная часть цикла. В то же время нельзя определять element и внутри, и снаружи цикла.

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

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

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

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

Тип данных element должен быть таким, чтобы тип данных элементов в group мог быть преобразован к нему.

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

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

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

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

Option Strict On 

Module Module1
    Sub Main()
        ' 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

        Console.ReadKey()
    End Sub 
End Module

Вызовы IEnumerator

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

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

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

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

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

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

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

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

Пример

В следующем примере перечислены все папки в папке 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

Следующий пример демонстрирует процедуру сортировки коллекции. Пример сортирует экземпляры класса Car, хранящихся в List. Класс Car реализует интерфейс IComparable, который требует, чтобы метод CompareTo был реализован.

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

В методе ListCars оператор cars.Sort() сортирует список. Этот вызов метода Sort List приводит к тому, что метод 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

См. также

Ссылки

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

Оператор While... End While (Visual Basic)

Оператор Do...Loop (Visual Basic)

Основные понятия

Циклические структуры (Visual Basic)

Расширяющие и сужающие преобразования (Visual Basic)

Инициализаторы объектов: именованные и анонимные типы (Visual Basic)

Инициализаторы коллекций (Visual Basic)

Другие ресурсы

Коллекции (C# и Visual Basic)

Массивы в Visual Basic