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

필수적 요소로서, Object 변수입니다. 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 문에서 선언해야 합니다. As 문을 사용하여 element의 형식을 명시적으로 선언할 수도 있고 형식을 할당하는 형식 유추에 의존할 수도 있습니다. 어느 경우든 element의 범위는 루프의 본문입니다. 그러나 루프 외부와 내부 모두에 element를 선언할 수는 없습니다.

필요에 따라 Next 문에 element를 지정할 수 있습니다. 이렇게 하면 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 문 다음의 문으로 이동합니다.

For Each 루프에 임의 수의 Exit For 문을 배치할 수 있습니다. 중첩된 For Each 루프 내에 Exit For를 사용하면 Exit For는 가장 안쪽의 루프를 끝내고 중첩 수준이 그 다음으로 높은 루프에 제어를 전달합니다.

Exit For는 If...Then...Else 구조와 마찬가지로 주로 일부 조건을 계산한 후에 사용됩니다. 다음 조건에 대해 Exit For를 사용할 수 있습니다.

  • 계속 반복은 불필요하며 불가능합니다. 오류가 있는 값 또는 종료 요청이 원인일 수 있습니다.

  • Try...Catch...Finally에 예외가 catch되었습니다. Finally 블록 끝에 Exit For를 사용할 수 있습니다.

  • 매우 많이 또는 무한정 실행되는 루프인 무한 루프가 있습니다. 이러한 조건을 감지한 경우 Exit For를 사용하여 루프를 이스케이프할 수 있습니다. 자세한 내용은 Do...Loop 문(Visual Basic)을 참조하십시오.

Continue For 문은 제어를 루프의 다음 반복으로 바로 이동합니다. 자세한 내용은 Continue 문(Visual Basic)을 참조하십시오.

데이터 형식

element의 데이터 형식은 group 요소의 데이터 형식을 변환할 수 있는 형식이어야 합니다.

group의 데이터 형식은 열거 가능한 컬렉션이나 배열을 참조하는 참조 형식이어야 합니다. 대부분 group은 [System.Collections] 네임스페이스의 T:System.Collections.IEnumerable 인터페이스 또는 [System.Collections.Generic] 네임스페이스의 T:System.Collections.Generic.IEnumerable`1 인터페이스를 구현하는 개체를 참조한다는 뜻입니다. System.Collections.IEnumerableGetEnumerator 메서드를 정의하여, 컬렉션의 열거자 개체를 반환합니다. 열거자 개체는 System.Collections 네임스페이스의 System.Collections.IEnumerator 인터페이스를 구현하며 Current 속성과 ResetMoveNext 메서드를 노출합니다. Visual Basic에서는 이를 사용하여 컬렉션을 순회합니다.

group의 요소는 대체로 Object 형식이지만 모든 런타임 데이터 형식을 가질 수 있습니다.

축소 변환

Option Strict가 On으로 설정된 경우 축소 변환을 수행하면 대개 컴파일 오류가 발생합니다. 하지만 For Each 문에서는 group의 요소에서 element로의 변환이 런타임에 계산되고 수행되므로 변환 축소로 인한 컴파일러 오류가 표시되지 않습니다.

다음 예제에서 보듯이 Long에서 Integer로의 변환은 축소 변환이므로 Option Strict가 설정되어 있을 때 m을 n의 초기 값으로 할당하면 컴파일 오류가 발생합니다. 하지만 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이 올바른 컬렉션 개체를 참조하는지 확인합니다. 올바른 컬렉션 개체를 참조하지 않는 경우 예외가 throw됩니다. 그렇지 않으면 열거자 개체의 MoveNext 메서드와 Current 속성을 호출하여 첫 번째 요소를 반환합니다. MoveNext가 다음 요소가 없음을 나타내면, 즉 컬렉션이 비어 있으면 For Each 루프가 종료되고 Next 문 다음 문으로 제어가 전달됩니다. 그렇지 않으면 Visual Basic에서 element를 첫 번째 요소로 설정하고 문 블록을 실행합니다.

Visual Basic에서는 Next 문을 만날 때마다 For Each 문으로 돌아갑니다. 다시 MoveNextCurrent를 호출하여 다음 요소를 반환하고 결과에 따라 블록을 다시 실행하거나 루프를 종료합니다. 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

앞의 예제에서는 thisControl 자체는 수정할 수 없어도 각 thisControl 요소의 BackColor 멤버는 수정할 수 있습니다.

배열 탐색. 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

다음 예제에서는 DirectoryInfo 클래스를 사용하여 C:\ 디렉터리에 있는 모든 폴더를 표시합니다.

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년 12월

비고 섹션을 재구성하고 예제를 추가했습니다.

향상된 기능 관련 정보