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

Повторяет группу операторов для каждого элемента в коллекции.Repeats a group of statements for each element in a collection.

СинтаксисSyntax

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

ЧастиParts

ТерминTerm ОпределениеDefinition
element Требуется в операторе For Each.Required in the For Each statement. Необязательно в операторе Next.Optional in the Next statement. Перемен.Variable. Используется для прохода по элементам коллекции.Used to iterate through the elements of the collection.
datatype Необязателен, если Option Infer имеет значение On (по умолчанию) или element уже объявлено; требуется, если Option Infer отключена и element еще не объявлена.Optional if Option Infer is on (the default) or element is already declared; required if Option Infer is off and element isn't already declared. Тип данных element.The data type of element.
group Обязательный.Required. Переменная типа, которая является типом коллекции или объектом.A variable with a type that's a collection type or Object. Ссылается на коллекцию, для которой необходимо повторить statements.Refers to the collection over which the statements are to be repeated.
statements Необязательный параметр.Optional. Одна или несколько инструкций между For Each и Next, которые выполняются для каждого элемента в group.One or more statements between For Each and Next that run on each item in group.
Continue For Необязательный параметр.Optional. Передает управление в начало цикла For Each.Transfers control to the start of the For Each loop.
Exit For Необязательный параметр.Optional. Передает управление из цикла For Each.Transfers control out of the For Each loop.
Next Обязательный.Required. Завершает определение цикла For Each.Terminates the definition of the For Each loop.

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

Если необходимо повторить набор инструкций для каждого элемента коллекции или массива, используйте цикл For Each... Next.Use a For Each...Next loop when you want to repeat a set of statements for each element of a collection or array.

Совет

A для... Оператор Next работает хорошо, когда можно связать каждую итерацию цикла с управляющей переменной и определить начальные и конечные значения переменной.A For...Next Statement works well when you can associate each iteration of a loop with a control variable and determine that variable's initial and final values. Однако при работе с коллекцией концепция начальных и конечных значений не имеет смысла, и вам не обязательно быть уверенным, сколько элементов имеет коллекция.However, when you are dealing with a collection, the concept of initial and final values isn't meaningful, and you don't necessarily know how many elements the collection has. В этом случае цикл For Each... Next часто является лучшим выбором.In this kind of case, a For Each...Next loop is often a better choice.

В следующем примере For Each... NextIn the following example, the For EachNext выполняет перебор всех элементов коллекции списков.statement iterates through all the elements of a List collection.

' 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 more examples, see Collections and Arrays.

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

Можно вложить циклы For Each, поместив один цикл в другой.You can nest For Each loops by putting one loop within another.

В следующем примере демонстрируется вложенная For Each... NextThe following example demonstrates nested For EachNext сотрудник.structures.

' 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.When you nest loops, each loop must have a unique element variable.

Можно также вкладывать различные виды управляющих структур друг в друга.You can also nest different kinds of control structures within each other. Дополнительные сведения см. в разделе вложенные структуры управления.For more information, see Nested Control Structures.

Выход и продолжение дляExit For and Continue For

Оператор Exit For вызывает завершение выполнения For... NextThe Exit For statement causes execution to exit the ForNext выполняет цикл и передает управление оператору, который следует за оператором Next.loop and transfers control to the statement that follows the Next statement.

Оператор Continue For передает управление сразу на следующую итерацию цикла.The Continue For statement transfers control immediately to the next iteration of the loop. Дополнительные сведения см. в разделе оператор continue.For more information, see Continue Statement.

В следующем примере показано, как использовать инструкции Continue For и Exit For.The following example shows how to use the Continue For and Exit For statements.

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

В цикле For Each можно разместить любое количество инструкций Exit For.You can put any number of Exit For statements in a For Each loop. При использовании внутри вложенных циклов For Each Exit For приводит к завершению самого внутреннего цикла и передает управление следующему более высокому уровню вложенности.When used within nested For Each loops, Exit For causes execution to exit the innermost loop and transfers control to the next higher level of nesting.

Exit For часто используется после оценки некоторого условия, например в структуре If... Then... Else.Exit For is often used after an evaluation of some condition, for example, in an If...Then...Else structure. Вы можете использовать Exit For в следующих случаях:You might want to use Exit For for the following conditions:

  • Продолжение итерации не требуется или невозможно.Continuing to iterate is unnecessary or impossible. Это может быть вызвано ошибочным значением или запросом на завершение.This might be caused by an erroneous value or a termination request.

  • Исключение перехвачено в Try... Catch... Finally. В конце блока Finally можно использовать Exit For.An exception is caught in a Try...Catch...Finally. You might use Exit For at the end of the Finally block.

  • Существует бесконечный цикл, который может выполняться с большим или даже бесконечным числом раз.There an endless loop, which is a loop that could run a large or even infinite number of times. Если обнаруживается такое условие, можно использовать Exit For для экранирования цикла.If you detect such a condition, you can use Exit For to escape the loop. Дополнительные сведения см. в разделе Do... Loop, инструкция.For more information, see Do...Loop Statement.

IteratorsIterators

Итератор используется для выполнения пользовательской итерации по коллекции.You use an iterator to perform a custom iteration over a collection. Итератор может быть функцией или методом доступа Get.An iterator can be a function or a Get accessor. Он использует оператор Yield для возвращения каждого элемента коллекции по одному за раз.It uses a Yield statement to return each element of the collection one at a time.

Итератор вызывается с помощью оператора For Each...Next.You call an iterator by using a For Each...Next statement. Каждая итерация цикла For Each вызывает итератор.Each iteration of the For Each loop calls the iterator. При достижении оператора Yield в итераторе возвращается выражение в операторе Yield, а текущее расположение в коде сохраняется.When a Yield statement is reached in the iterator, the expression in the Yield statement is returned, and the current location in code is retained. При следующем вызове итератора выполнение возобновляется с этого места.Execution is restarted from that location the next time that the iterator is called.

В следующем примере используется функция итератора.The following example uses an iterator function. Функция итератора имеет оператор Yield, который находится внутри блока for... Следующий цикл.The iterator function has a Yield statement that's inside a For…Next loop. В методе ListEvenNumbers каждая итерация тела оператора For Each создает вызов функции-итератора, который переходит к следующей инструкции Yield.In the ListEvenNumbers method, each iteration of the For Each statement body creates a call to the iterator function, which proceeds to the next Yield statement.

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

Дополнительные сведения см. в разделе итераторы, оператор yieldи итератор.For more information, see Iterators, Yield Statement, and Iterator.

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

Когда For Each... NextWhen a For EachNext выполняется инструкция, Visual Basic вычисляет коллекцию только один раз перед началом цикла.statement runs, Visual Basic evaluates the collection only one time, before the loop starts. Если блок инструкции изменяет element или group, эти изменения не влияют на итерацию цикла.If your statement block changes element or group, these changes don't affect the iteration of the loop.

Когда все элементы в коллекции последовательно назначаются element, цикл For Each останавливается и управление передается оператору, следующему за оператором Next.When all the elements in the collection have been successively assigned to element, the For Each loop stops and control passes to the statement following the Next statement.

Если параметр Infer имеет значение On (значение по умолчанию), то компилятор Visual Basic может определить тип данных element.If Option Infer is on (its default setting), the Visual Basic compiler can infer the data type of element. Если он выключен и element не объявлен за пределами цикла, необходимо объявить его в операторе For Each.If it is off and element hasn't been declared outside the loop, you must declare it in the For Each statement. Чтобы явно объявить тип данных element, используйте предложение As.To declare the data type of element explicitly, use an As clause. Если тип данных элемента не определен за пределами конструкции For Each... Next, ее областью является тело цикла.Unless the data type of element is defined outside the For Each...Next construct, its scope is the body of the loop. Обратите внимание, что нельзя объявлять element как вне, так и внутри цикла.Note that you cannot declare element both outside and inside the loop.

При необходимости можно указать element в операторе Next.You can optionally specify element in the Next statement. Это повышает удобочитаемость программы, особенно если у вас есть вложенные циклы For Each.This improves the readability of your program, especially if you have nested For Each loops. Необходимо указать ту же переменную, которая отображается в соответствующей инструкции For Each.You must specify the same variable as the one that appears in the corresponding For Each statement.

Может возникнуть необходимость избежать изменения значения element внутри цикла.You might want to avoid changing the value of element inside a loop. Это может усложнить чтение и отладку кода.Doing this can make it more difficult to read and debug your code. Изменение значения group не влияет на коллекцию или ее элементы, которые были определены при первом входе в цикл.Changing the value of group doesn't affect the collection or its elements, which were determined when the loop was first entered.

Если в качестве вложенных циклов используется инструкция Next внешнего уровня вложенности перед Next внутреннего уровня, то компилятор сигнализирует об ошибке.When you're nesting loops, if a Next statement of an outer nesting level is encountered before the Next of an inner level, the compiler signals an error. Однако компилятор может обнаружить эту перекрытие ошибки только в том случае, если в каждой инструкции Next указано значение element.However, the compiler can detect this overlapping error only if you specify element in every Next statement.

Если код зависит от прохода по коллекции в определенном порядке, то цикл For Each... Next не является лучшим выбором, если только вы не знакомы с характеристиками объекта перечислителя, предоставляемого коллекцией.If your code depends on traversing a collection in a particular order, a For Each...Next loop isn't the best choice, unless you know the characteristics of the enumerator object the collection exposes. Порядок обхода не определяется Visual Basic, а методом MoveNext объекта перечислителя.The order of traversal isn't determined by Visual Basic, but by the MoveNext method of the enumerator object. Таким образом, вы не сможете предсказать, какой элемент коллекции первым должен возвращаться в element, или что является следующим, возвращаемым после заданного элемента.Therefore, you might not be able to predict which element of the collection is the first to be returned in element, or which is the next to be returned after a given element. Вы можете достичь более надежных результатов, используя другую структуру цикла, например For... Next или Do... Loop.You might achieve more reliable results using a different loop structure, such as For...Next or Do...Loop.

Среда выполнения должна иметь возможность преобразования элементов в group в element.The runtime must be able to convert the elements in group to element. Оператор [Option Strict] определяет, допускаются ли расширяющие и сужающие преобразования (Option Strict отключено, значение по умолчанию) или разрешены только расширяющие преобразования (Option Strict включено).The [Option Strict] statement controls whether both widening and narrowing conversions are allowed (Option Strict is off, its default value), or whether only widening conversions are allowed (Option Strict is on). Дополнительные сведения см. в разделе сужающие преобразования.For more information, see Narrowing conversions.

Тип данных group должен быть ссылочным типом, ссылающимся на коллекцию или массив, который является перечислимым.The data type of group must be a reference type that refers to a collection or an array that's enumerable. Чаще всего это означает, что group относится к объекту, который реализует интерфейс IEnumerable пространства имен System.Collections или интерфейс IEnumerable<T> пространства имен System.Collections.Generic.Most commonly this means that group refers to an object that implements the IEnumerable interface of the System.Collections namespace or the IEnumerable<T> interface of the System.Collections.Generic namespace. System.Collections.IEnumerable определяет метод GetEnumerator, который возвращает объект перечислителя для коллекции.System.Collections.IEnumerable defines the GetEnumerator method, which returns an enumerator object for the collection. Объект перечислителя реализует интерфейс System.Collections.IEnumerator пространства имен System.Collections и предоставляет свойство Current и методы Reset и MoveNext.The enumerator object implements the System.Collections.IEnumerator interface of the System.Collections namespace and exposes the Current property and the Reset and MoveNext methods. Visual Basic использует их для прохода по коллекции.Visual Basic uses these to traverse the collection.

сужающие преобразованияNarrowing Conversions

Если Option Strict имеет значение On, сужающие преобразования обычно приводят к ошибкам компилятора.When Option Strict is set to On, narrowing conversions ordinarily cause compiler errors. Однако в операторе For Each преобразования из элементов group в element оцениваются и выполняются во время выполнения, а ошибки компилятора, вызванные сужающими преобразованиями, подавляются.In a For Each statement, however, conversions from the elements in group to element are evaluated and performed at run time, and compiler errors caused by narrowing conversions are suppressed.

В следующем примере присваивание m в качестве начального значения для n не компилируется, если Option Strict имеет значение ON, так как преобразование Long в Integer является узким преобразованием.In the following example, the assignment of m as the initial value for n doesn't compile when Option Strict is on because the conversion of a Long to an Integer is a narrowing conversion. Однако в операторе For Each не сообщается об ошибке компилятора, хотя для присваивания number требуется такое же преобразование из Long в Integer.In the For Each statement, however, no compiler error is reported, even though the assignment to number requires the same conversion from Long to Integer. В инструкции For Each, содержащей большое число, ошибка времени выполнения возникает при применении ToInteger к большому числу.In the For Each statement that contains a large number, a run-time error occurs when ToInteger is applied to the large number.

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

Вызовы IEnumeratorIEnumerator Calls

Когда начинается выполнение цикла For Each... Next, Visual Basic проверяет, что group ссылается на допустимый объект коллекции.When execution of a For Each...Next loop starts, Visual Basic verifies that group refers to a valid collection object. В противном случае вызывается исключение.If not, it throws an exception. В противном случае он вызывает метод MoveNext и свойство Current объекта перечислителя для возврата первого элемента.Otherwise, it calls the MoveNext method and the Current property of the enumerator object to return the first element. Если MoveNext указывает, что следующий элемент отсутствует, то есть если коллекция пуста, цикл For Each останавливается и управление передается оператору, следующему за оператором Next.If MoveNext indicates that there is no next element, that is, if the collection is empty, the For Each loop stops and control passes to the statement following the Next statement. В противном случае Visual Basic задает значение element для первого элемента и выполняет блок операторов.Otherwise, Visual Basic sets element to the first element and runs the statement block.

Каждый раз, когда Visual Basic встречает оператор Next, он возвращается к оператору For Each.Each time Visual Basic encounters the Next statement, it returns to the For Each statement. Снова вызывается MoveNext и Current, чтобы вернуть следующий элемент, и снова он либо выполняет блок, либо останавливает цикл в зависимости от результата.Again it calls MoveNext and Current to return the next element, and again it either runs the block or stops the loop depending on the result. Этот процесс будет продолжен до тех пор, пока MoveNext указывает, что следующий элемент отсутствует или не обнаружен оператор Exit For.This process continues until MoveNext indicates that there is no next element or an Exit For statement is encountered.

Изменение коллекции.Modifying the Collection. Объект перечислителя, возвращаемый GetEnumerator, обычно не позволяет изменить коллекцию, добавляя, удаляя, заменяя или переупорядочивая любые элементы.The enumerator object returned by GetEnumerator normally doesn't let you change the collection by adding, deleting, replacing, or reordering any elements. При изменении коллекции после запуска цикла For Each... Next объект перечислителя станет недопустимым, а следующая попытка доступа к элементу вызовет исключение InvalidOperationException.If you change the collection after you have initiated a For Each...Next loop, the enumerator object becomes invalid, and the next attempt to access an element causes an InvalidOperationException exception.

Однако эта блокировка изменений не определяется Visual Basic, а реализацией интерфейса IEnumerable.However, this blocking of modification isn't determined by Visual Basic, but rather by the implementation of the IEnumerable interface. Можно реализовать IEnumerable таким образом, чтобы можно было изменять во время итерации.It is possible to implement IEnumerable in a way that allows for modification during iteration. Если вы планируете выполнять такие динамические изменения, убедитесь, что понимаете характеристики реализации IEnumerable в используемой коллекции.If you are considering doing such dynamic modification, make sure that you understand the characteristics of the IEnumerable implementation on the collection you are using.

Изменение элементов коллекции.Modifying Collection Elements. Свойство Current объекта перечислителя имеет значение ReadOnlyи возвращает локальную копию каждого элемента коллекции.The Current property of the enumerator object is ReadOnly, and it returns a local copy of each collection element. Это означает, что нельзя изменить сами элементы в цикле For Each... Next.This means that you cannot modify the elements themselves in a For Each...Next loop. Любые изменения влияют только на локальную копию из Current и не отражаются обратно в базовую коллекцию.Any modification you make affects only the local copy from Current and isn't reflected back into the underlying collection. Однако если элемент является ссылочным типом, можно изменить члены экземпляра, на который он указывает.However, if an element is a reference type, you can modify the members of the instance to which it points. В следующем примере изменяется элемент BackColor каждого элемента thisControl.The following example modifies the BackColor member of each thisControl element. Однако вы не можете изменить thisControl.You cannot, however, modify thisControl itself.

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.The previous example can modify the BackColor member of each thisControl element, although it cannot modify thisControl itself.

Обход массивов.Traversing Arrays. Поскольку класс Array реализует интерфейс IEnumerable, все массивы предоставляют метод GetEnumerator.Because the Array class implements the IEnumerable interface, all arrays expose the GetEnumerator method. Это означает, что можно выполнить итерацию по массиву с помощью цикла For Each... Next.This means that you can iterate through an array with a For Each...Next loop. Однако можно только считывать элементы массива.However, you can only read the array elements. Их нельзя изменить.You cannot change them.

ПримерExample

В следующем примере выводится список всех папок в папке C:. каталог с помощью класса DirectoryInfo.The following example lists all the folders in the C:\ directory by using the DirectoryInfo class.

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

ПримерExample

Приведенный ниже пример демонстрирует процедуру сортировки коллекции.The following example illustrates a procedure for sorting a collection. В примере выполняется сортировка экземпляров класса Car, которые хранятся в List<T>.The example sorts instances of a Car class that are stored in a List<T>. Класс Car реализует интерфейс IComparable<T>, который требует реализации метода CompareTo.The Car class implements the IComparable<T> interface, which requires that the CompareTo method be implemented.

Каждый вызов метода CompareTo выполняет одно сравнение, которое используется для сортировки.Each call to the CompareTo method makes a single comparison that's used for sorting. Написанный пользователем код в методе CompareTo возвращает значение для каждого сравнения текущего объекта с другим объектом.User-written code in the CompareTo method returns a value for each comparison of the current object with another object. Возвращаемое значение меньше нуля, если текущий объект меньше другого объекта, больше нуля, если текущий объект больше другого объекта, и равняется нулю, если объекты равны.The value returned is less than zero if the current object is less than the other object, greater than zero if the current object is greater than the other object, and zero if they are equal. Это позволяет определить в коде условия для отношения «больше», «меньше» и «равно».This enables you to define in code the criteria for greater than, less than, and equal.

В методе ListCars оператор cars.Sort() сортирует список.In the ListCars method, the cars.Sort() statement sorts the list. Этот вызов метода Sort List<T> приводит к тому, что метод CompareTo вызывается автоматически для объектов Car в List.This call to the Sort method of the List<T> causes the CompareTo method to be called automatically for the Car objects in the 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

См. такжеSee also