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

Visual Basic вычисляет коллекцию только один раз — перед началом цикла. Если в блоке операторов изменяется element или group, то эти изменения не оказывает влияния на повторение цикла.

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

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

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

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

Совет

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

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

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

Циклы For Each могут вкладываться друг в друга. При этом каждый цикл должен иметь уникальную переменную element.

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

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

Exit For

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

Любое число операторов 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).

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

Типы данных

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

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

Элементы group обычно относятся к типу Object, но могут также иметь любой тип данных времени выполнения.

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

Если 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 и не отражается на коллекции. Тем не менее, если элемент имеет ссылочный тип, то можно изменять члены экземпляра, на который он указывает. Это демонстрируется в следующем примере.

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 можно перебирать и массивы. Тем не менее, элементы массива можно только считывать. Это изменить нельзя. Пример см. в разделе Практическое руководство. Запуск нескольких операторов для каждого элемента в коллекции или массиве (Visual Basic).

Пример

В следующем примере показан способ использования оператора 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

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

В следующем примере показано, как используются операторы 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

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

В этом примере демонстрируется сортировка коллекции с помощью интерфейса IComparable.

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
        ' Determine the relative order of the objects being compared.
        ' This is used to 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

Следующий пример включает в себя пользовательский класс коллекции, имеющий пользовательский перечислитель.

Public Sub ListColors()
    Dim colors As New AllColors()

    For Each theColor As Color In colors
        Debug.Write(theColor.Name & " ")
    Next
    Debug.WriteLine("")
    ' Output: red blue green
End Sub

' Collection class.
Public Class AllColors
    Implements System.Collections.IEnumerable

    Private _colors() As Color =
    {
        New Color With {.Name = "red"},
        New Color With {.Name = "blue"},
        New Color With {.Name = "green"}
    }

    Public Function GetEnumerator() As System.Collections.IEnumerator _
        Implements System.Collections.IEnumerable.GetEnumerator

        Return New ColorEnumerator(_colors)

        ' Instead of creating a using a custom enumerator, you could
        ' use the GetEnumerator of the array.
        'Return _colors.GetEnumerator
    End Function

    ' Custom enumerator.
    Private Class ColorEnumerator
        Implements System.Collections.IEnumerator

        Private _colors() As Color
        Private _position As Integer = -1

        Public Sub New(ByVal colors() As Color)
            _colors = colors
        End Sub

        Public ReadOnly Property Current() As Object Implements System.Collections.IEnumerator.Current
            Get
                Return _colors(_position)
            End Get
        End Property

        Public Function MoveNext() As Boolean Implements System.Collections.IEnumerator.MoveNext
            _position += 1
            Return (_position < _colors.Length)
        End Function

        Public Sub Reset() Implements System.Collections.IEnumerator.Reset
            _position = -1
        End Sub
    End Class
End Class

' Element class.
Public Class Color
    Public Property Name As String
End Class

См. также

Задачи

Практическое руководство. Запуск нескольких операторов для каждого элемента в коллекции или массиве (Visual Basic)

Ссылки

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

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

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

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

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

Коллекции в Visual Basic

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

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

Общие сведения о инициализаторах наборов (Visual Basic)

Массивы в Visual Basic

Журнал изменений

Дата

Журнал

Причина

Декабрь 2010

Реорганизованы примечания и добавлены примеры.

Улучшение информации.