Instrução For Each...Next (Visual Basic)
Repete um grupo de instruções para cada elemento em uma coleção.
Sintaxe
For Each element [ As datatype ] In group
[ statements ]
[ Continue For ]
[ statements ]
[ Exit For ]
[ statements ]
Next [ element ]
Partes
Termo | Definição |
---|---|
element |
Obrigatório na instrução For Each . Opcional na instrução Next . Variável. Usada para iterar por meio dos elementos da coleção. |
datatype |
Opcional se Option Infer estiver ativado (o padrão) ou element já estiver declarado; obrigatório se Option Infer estiver desativado e element ainda não estiver declarado. O tipo de dados de element . |
group |
Obrigatórios. Uma variável com um tipo que é um tipo de coleção ou Objeto. Refere-se à coleção sobre a qual as statements devem ser repetidas. |
statements |
Opcional. Uma ou mais instruções entre For Each e Next que são executadas em cada item em group . |
Continue For |
Opcional. Transfere o controle para o início do loop For Each . |
Exit For |
Opcional. Transfere o controle para fora do loop For Each . |
Next |
Obrigatórios. Finaliza a definição do loop For Each . |
Exemplo simples
Use um loop For Each
...Next
quando quiser repetir um conjunto de instruções para cada elemento de uma coleção ou matriz.
Dica
A Instrução For...Next funciona bem quando você pode associar cada iteração de um loop a uma variável de controle e determinar os valores iniciais e finais dessa variável. No entanto, quando você está lidando com uma coleção, o conceito de valores iniciais e finais não é significativo e você não sabe necessariamente quantos elementos a coleção tem. Nesse tipo de caso, um loop For Each
...Next
muitas vezes é uma escolha melhor.
No exemplo a seguir, a instrução For Each
…Next
itera por meio de todos os elementos de uma coleção 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
Para obter mais exemplos, confira Coleções e Matrizes.
Nested Loops
Você pode aninhar loops For Each
colocando um loop dentro de outro.
O exemplo a seguir demonstra estruturas For Each
…Next
aninhadas.
' 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
Quando você aninha loops, cada loop deve ter uma variável element
exclusiva.
Você também pode aninhar diferentes tipos de estruturas de controle entre si. Para obter mais informações, confira Estruturas de controle aninhadas.
Exit For e Continue For
A instrução Exit For faz com que a execução saia do loop For
...Next
e transfere o controle para a instrução que segue a instrução Next
.
A instruçãoContinue For
transfere o controle imediatamente para a próxima iteração do loop. Para obter mais informações, confira Instrução Continue.
O exemplo a seguir mostra como usar as instruções Continue For
e 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
Você pode colocar qualquer número de instruções Exit For
em um loop For Each
. Quando usado em loops For Each
aninhados, Exit For
faz com que a execução saia do loop mais interno e transfere o controle para o próximo nível mais alto de aninhamento.
Exit For
geralmente é usado após uma avaliação de alguma condição, por exemplo, em uma estrutura If
...Then
...Else
. Talvez você queira usar Exit For
para as seguintes condições:
Continuar a iterar é desnecessário ou impossível. Isso pode ser causado por um valor incorreto ou uma solicitação de encerramento.
Uma exceção é capturada em um
Try
...Catch
...Finally
. Você pode usarExit For
no final do blocoFinally
.Há um loop infinito, que é um loop que pode se executado várias vezes ou até infinitamente. Se você detectar essa condição, poderá usar
Exit For
para pular o loop. Para obter mais informações, confira Instrução Do...Loop.
Iterators
Use um iterador para realizar uma iteração personalizada em uma coleção. Um iterador pode ser uma função ou um acessador Get
. Ele usa uma instrução Yield
para retornar um elemento da coleção por vez.
Chame um iterador usando uma instrução For Each...Next
. Cada iteração do loop For Each
chama o iterador. Quando uma instrução Yield
é alcançada no iterador, a expressão na instrução Yield
é retornada e o local atual no código é retido. A execução será reiniciada desse local na próxima vez que o iterador for chamado.
O exemplo a seguir usa uma função iteradora. A função iteradora tem uma instrução Yield
que está em um loop For…Next. No método ListEvenNumbers
, cada iteração do corpo da instrução For Each
cria uma chamada à função iteradora, que avança para a próxima instrução 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
Para obter mais informações, confira Iteradores, Instrução Yield e Iterador.
Implementação Técnica
Quando uma instrução For Each
…Next
é executada, o Visual Basic avalia a coleção apenas uma vez, antes do loop ser iniciado. Se o bloco de instruções alterar element
ou group
, essas alterações não afetarão a iteração do loop.
Quando todos os elementos na coleção foram atribuídos a element
sucessivamente, o loop For Each
para e o controle passa para a instrução após a instrução Next
.
Se Option Infer estiver ativado (configuração padrão), o compilador do Visual Basic poderá inferir o tipo de dados de element
. Se ele estiver desativado e element
não tiver sido declarado fora do loop, você deverá declará-lo na instrução For Each
. Para declarar o tipo de dados de element
explicitamente, use uma cláusula As
. A menos que o tipo de dados do elemento seja definido fora do constructo For Each
...Next
, o escopo é o corpo do loop. Observe que você não pode declarar element
fora e dentro do loop.
Como opção, você pode especificar element
na instrução Next
. Isso melhora a legibilidade do programa, especialmente se você tiver loops For Each
aninhados. Você precisa especificar a mesma variável que a exibida na instrução For Each
correspondente.
Talvez você queira evitar alterar o valor de element
dentro de um loop. Fazer isso pode dificultar a leitura e a depuração do código. Alterar o valor de group
não afeta a coleção nem os elementos dela, que foram determinados quando o loop foi inserido pela primeira vez.
Quando você está aninhando loops, se uma instrução Next
de um nível de aninhamento externo for encontrada antes do Next
de nível interno, o compilador sinalizará um erro. No entanto, o compilador só poderá detectar esse erro sobreposto se você especificar element
em cada instrução Next
.
Se o código depender de percorrer uma coleção em uma determinada ordem, um loop For Each
...Next
não será a melhor opção, a menos que você saiba as características do objeto enumerador que a coleção expõe. A ordem de passagem não é determinada pelo Visual Basic, mas pelo método MoveNext do objeto enumerador. Portanto, talvez não seja possível prever qual elemento da coleção é o primeiro a ser retornado em element
ou qual é o próximo a ser retornado após determinado elemento. Você pode obter resultados mais confiáveis usando uma estrutura de loop diferente, como For
...Next
ou Do
...Loop
.
O runtime deve ser capaz de converter os elementos em group
em element
. A instrução [Option Strict
] controla se as conversões de expansão e restrição são permitidas (Option Strict
está desativada, valor padrão) ou se somente conversões de expansão são permitidas (Option Strict
está ativada). Para obter mais informações, confira Conversões de restrição.
O tipo de dados de group
deve ser um tipo de referência que se refere a uma coleção ou a uma matriz enumerável. Mais comumente isso significa que group
se refere a um objeto que implementa a interface IEnumerable do namespace System.Collections
ou a interface IEnumerable<T> do namespace System.Collections.Generic
. System.Collections.IEnumerable
define o método GetEnumerator, que retorna um objeto enumerador para a coleção. O objeto enumerador implementa a interface System.Collections.IEnumerator
do namespace System.Collections
e expõe a propriedade Current e os métodos Reset e MoveNext. O Visual Basic os usa para percorrer a coleção.
Conversões de redução
Quando Option Strict
é definido como On
, as conversões de restrição normalmente causam erros do compilador. No entanto, em uma instrução For Each
, as conversões dos elementos em group
em element
são avaliadas e executadas em tempo de execução, e os erros do compilador causados pelas conversões de restrição são suprimidos.
No exemplo a seguir, a atribuição de m
como o valor inicial para n
não é compilada quando Option Strict
está ativada porque a conversão de um Long
em um Integer
é uma conversão de restrição. No entanto, na instrução For Each
, nenhum erro do compilador é relatado, embora a atribuição para number
exigir a mesma conversão de Long
em Integer
. Na instrução For Each
que contém um número grande, ocorre um erro em tempo de execução quando ToInteger é aplicado ao número grande.
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
Chamadas IEnumerator
Quando a execução de um loop For Each
...Next
é iniciada, o Visual Basic verifica se group
se refere a um objeto de coleção válido. Se não for, ele vai gerar uma exceção. Caso contrário, ele chama o método MoveNext e a propriedade Current do objeto enumerador para retornar o primeiro elemento. Se MoveNext
indicar que não há nenhum próximo elemento, ou seja, se a coleção estiver vazia, o loop For Each
será interrompido e o controle passará para a instrução após a instrução Next
. Caso contrário, o Visual Basic define element
como o primeiro elemento e executa o bloco de instruções.
Sempre que o Visual Basic encontra a instrução Next
, ela retorna à instrução For Each
. Novamente, ele chama MoveNext
e Current
para retornar o próximo elemento e, novamente, executa o bloco ou interrompe o loop dependendo do resultado. Esse processo continua até MoveNext
indicar que não há nenhum próximo elemento ou uma instrução Exit For
encontrada.
Modificando a coleção. O objeto enumerador retornado por GetEnumerator normalmente não permite alterar a coleção adicionando, excluindo, substituindo ou reordenando elementos. Se você alterar a coleção depois de iniciar um loop For Each
...Next
, o objeto enumerador se tornará inválido e a próxima tentativa de acessar um elemento causará uma exceção InvalidOperationException.
No entanto, esse bloqueio de modificação não é determinado pelo Visual Basic, mas sim pela implementação da interface IEnumerable. É possível implementar IEnumerable
de uma forma que permita a modificação durante a iteração. Se você estiver considerando fazer essa modificação dinâmica, certifique-se de entender as características da implementação IEnumerable
na coleção que você está usando.
Modificando elementos da coleção. A propriedade Current do objeto enumerador é ReadOnly e retorna uma cópia local de cada elemento da coleção. Isso significa que você não pode modificar os próprios elementos em um loop For Each
...Next
. Qualquer modificação feita afeta apenas a cópia local de Current
e não é refletida novamente na coleção subjacente. No entanto, se um elemento for um tipo de referência, você poderá modificar os membros da instância para a qual ele aponta. O exemplo a seguir modifica o membro BackColor
de cada elemento thisControl
. No entanto, você não pode modificar thisControl
em si.
Sub LightBlueBackground(thisForm As System.Windows.Forms.Form)
For Each thisControl In thisForm.Controls
thisControl.BackColor = System.Drawing.Color.LightBlue
Next thisControl
End Sub
O exemplo anterior pode modificar o membro BackColor
de cada elemento thisControl
, embora ele não possa modificar thisControl
em si.
Percorrendo matrizes. Como a classe Array implementa a interface IEnumerable, todas as matrizes expõem o método GetEnumerator. Isso significa que você pode iterar por meio de uma matriz com um loop For Each
...Next
. No entanto, você só pode ler os elementos da matriz. Não é possível alterá-los.
Exemplo 1
O exemplo a seguir lista todas as pastas no C:\ diretório usando a classe DirectoryInfo.
Dim dInfo As New System.IO.DirectoryInfo("c:\")
For Each dir As System.IO.DirectoryInfo In dInfo.GetDirectories()
Debug.WriteLine(dir.Name)
Next
Exemplo 2
O exemplo a seguir ilustra um procedimento para a classificação de uma coleção. O exemplo classifica instâncias de uma classe Car
que estão armazenados em uma List<T>. A classe Car
implementa a interface IComparable<T>, que requer que o método CompareTo seja implementado.
Cada chamada ao método CompareTo faz uma comparação única que é usada para classificação. Os códigos escritos pelo usuário no método CompareTo
retornam um valor para cada comparação do objeto atual com outro objeto. O valor retornado será menor que zero se o objeto atual for menor que o outro objeto, maior que zero se o objeto atual for maior que o outro objeto e zero, se eles forem iguais. Isso permite que você defina no código os critérios para maior que, menor que e igual.
No método ListCars
, a instrução cars.Sort()
classifica a lista. Essa chamada para o método Sort da List<T> faz com que o método CompareTo
seja chamado automaticamente para os objetos Car
na 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