Оператор 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 Infer Option Infer отключен и element еще не объявлен. Тип данных element . |
group |
Обязательный. Переменная с типом, который является типом коллекции или object. Ссылается на коллекцию, над которой statements следует повторяться. |
statements |
Необязательно. Один или несколько операторов между For Each Next ими, выполняющимися на каждом элементе.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
.
Среда выполнения должна быть в состоянии преобразовать элементы в group
element
. Оператор [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
Однако в инструкции преобразования элементов, которые будут group
element
оцениваться и выполняться во время выполнения, и ошибки компилятора, вызванные сужающими преобразованиями, подавляются.
В следующем примере назначение m
в качестве начального значения n
не компилируется, Option Strict
так как преобразование объекта Long
в сужающее Integer
преобразование является сужающим. Однако в инструкции не сообщается об ошибке For Each
компилятора, несмотря на то, что назначение требует number
того же преобразования из Integer
Long
. 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