For Each...Next 문(Visual Basic)

업데이트: 2008년 7월

컬렉션의 각 요소에 대해 문의 그룹을 반복합니다.

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

구성 요소

  • element
    For Each 문에서는 필수적 요소입니다. Next 문에서는 선택적 요소입니다. 컬렉션의 요소를 반복하는 데 사용되는 변수입니다.

  • datatype
    element가 아직 선언되지 않은 경우 필수적 요소입니다. element의 데이터 형식입니다.

  • group
    필수적 요소. Object 변수입니다. statements를 반복할 컬렉션을 참조합니다.

  • statements
    선택적 요소. For Each와 Next 사이에서 group의 각 항목에 대해 실행되는 하나 이상의 문입니다.

  • Exit For
    선택적 요소. For Each 루프 밖으로 제어를 전달합니다.

  • Next
    필수적 요소. For Each 루프의 정의를 끝냅니다.

설명

컬렉션이나 배열의 각 요소에 대해 여러 문을 반복하려는 경우 For Each...Next 루프를 사용합니다.

For...Next 문(Visual Basic)은 각 루프의 반복을 제어 변수와 연결하고 해당 변수의 초기 값과 최종 값을 결정할 수 있는 경우에 제대로 작동합니다. 그러나 컬렉션을 처리할 때는 초기 값과 최종 값이라는 개념이 의미가 없으며 컬렉션의 요소 수를 반드시 알 필요는 없습니다. 이 경우에는 For Each...Next 루프가 더 적합합니다.

규칙

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

    group의 데이터 형식은 컬렉션이나 배열을 참조하는 참조 형식이어야 합니다. 즉 group은 System.Collections 네임스페이스의 IEnumerable 인터페이스 또는 System.Collections.Generic 네임스페이스의 IEnumerable<T> 인터페이스를 구현하는 개체를 참조해야 합니다. IEnumerable은 컬렉션에 대한 열거자 개체를 반환하는 GetEnumerator 메서드를 정의합니다. 열거자 개체는 System.Collections 네임스페이스의 IEnumerator 인터페이스를 구현하며 Current 속성과 ResetMoveNext 메서드를 노출합니다. Visual Basic에서는 이를 사용하여 컬렉션을 순회합니다.

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

  • **축소 변환.**Option Strict가 On으로 설정된 경우 축소 변환을 수행하면 대개 컴파일 오류가 발생합니다. 다음 예제에서 보듯이 Long에서 Integer로의 변환은 축소 변환이므로 Option Strict가 설정되어 있을 때 m을 n의 초기 값으로 할당하면 컴파일 오류가 발생합니다.

    Dim m As Long = 987
    ' Does not compile.
    'Dim n As Integer = m
    

    하지만 group의 요소에서 element로의 변환은 런타임에 계산되고 수행되므로 축소 변환 오류가 표시되지 않습니다. 다음 예제에서는 이전 예제에서 오류를 발생시킨 Long에서 Integer로의 변환이 있음에도 불구하고 For Each 루프에서 컴파일러 오류가 보고되지 않습니다.

    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. The output is 45 3 987.
            For Each p As Integer In New Long() {45, 3, 987}
                Console.Write(p & " ")
            Next
            Console.WriteLine()
        End Sub
    End Module
    
    

    컴파일러 오류가 없다고 해서 런타임 오류가 발생할 위험이 없어지는 것은 아닙니다. 다음 예제에서는 컴파일러 오류가 보고되지 않지만 ToInteger가 9876543210에 적용될 때 런타임 오류가 발생합니다. 런타임 오류는 Option Strict의 설정 여부에 관계없이 발생합니다.

    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 = 9876543210
            'Dim n As Integer = m
    
            Try
                ' The For Each loop requires the same conversion, but
                ' is not flagged by the compiler. A run-time error is 
                ' raised because 9876543210 is too large for type Integer.
                For Each p As Integer In New Long() {45, 3, 9876543210}
                    Console.Write(p & " ")
                Next
                Console.WriteLine()
            Catch e As System.OverflowException
                Console.WriteLine()
                Console.WriteLine(e.Message)
            End Try
        End Sub
    End Module
    
    
  • **선언.**element가 해당 루프의 외부에 선언되지 않았으면 For Each 문 내에서 선언해야 합니다. As 문을 사용하여 element의 형식을 명시적으로 선언할 수도 있고 형식을 할당하는 형식 유추에 의존할 수도 있습니다. 어느 경우든 element의 범위는 루프의 본문입니다. 그러나 루프 외부와 내부 모두에 element를 선언할 수는 없습니다.

  • 반복 횟수. Visual Basic에서는 컬렉션을 루프가 시작되기 전에 한 번만 계산합니다. 문 블록이 element 또는 group을 변경하는 경우 이 변경 내용은 루프의 반복에는 영향을 주지 않습니다.

  • 루프 중첩. 하나의 For Each 루프 안에 동일한 루프를 중첩할 수 있습니다. 그러나 이러한 경우 루프마다 고유한 element 변수가 있어야 합니다.

    종류가 서로 다른 제어 구조도 중첩할 수 있습니다. 자세한 내용은 중첩 제어 구조를 참조하십시오.

    참고:

    외부 중첩 수준의 Next 문이 내부 중첩 수준의 Next 앞에 있으면 컴파일러에서 오류가 발생합니다. 그러나 모든 Next 문에 element를 지정하는 경우에만 컴파일러에서 이러한 겹치는 오류를 검색할 수 있습니다.

  • 제어 변수 식별. 필요한 경우에 Next 문에 element를 지정할 수 있습니다. 이렇게 하면 For Each 루프가 중첩된 경우에 특히 프로그램의 가독성이 향상됩니다. 해당 For Each 문에 나타나는 변수와 동일한 변수를 지정해야 합니다.

  • 루프 밖으로 이동.Exit 문(Visual Basic)을 사용하면 제어가 즉시 Next 문 다음 문으로 이동됩니다. 예를 들어, 잘못된 값이나 종료 요청과 같이 계속 반복할 필요가 없거나 반복할 수 없는 조건을 발견하면 루프를 끝내야 할 수 있습니다. 또한 Try...Catch...Finally 문에서 예외를 catch하는 경우 Finally 블록의 끝에서 Exit For를 사용할 수 있습니다.

    원하는 수의 Exit For 문을 For Each 루프의 원하는 위치에 배치할 수 있습니다. Exit For는 If...Then...Else 구조에서처럼 일부 조건을 계산한 후에 사용될 수도 있습니다.

  • **무한 루프.**Exit For를 사용하여 조건이 무한 루프를 초래하는지 여부를 테스트할 수도 있습니다. 무한 루프란 아주 많이 또는 무한정 실행되는 루프를 말합니다. 이러한 조건이 검색되면 Exit For를 사용하여 루프를 벗어날 수 있습니다. 자세한 내용은 Do...Loop 문(Visual Basic)을 참조하십시오.

동작

  • **루프 시작.**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 문을 만날 때까지 이 과정이 계속됩니다.

  • 루프 종료. 컬렉션의 모든 요소가 element에 할당되었으면 For Each 루프가 종료되고 Next 문 다음 문으로 제어가 전달됩니다.

  • 반복 값 변경. 루프 안에 있는 동안 element의 값을 변경하면 코드를 읽고 디버깅하기가 더욱 어려워질 수 있습니다. group 값을 변경해도 컬렉션이나 해당 요소에는 영향이 없습니다. 이러한 사항은 루프가 처음 시작되었을 때 결정됩니다.

  • **탐색 순서.**For Each...Next 루프를 실행하는 경우 컬렉션 탐색은 GetEnumerator 메서드에서 반환하는 열거자 개체의 제어를 받습니다. 탐색 순서는 Visual Basic에서 결정되지 않고 열거자 개체의 MoveNext 메서드에 의해 결정됩니다. 이것은 element에서 처음 반환되는 컬렉션 요소나 지정된 요소 다음에 반환되는 컬렉션 요소를 예측할 수 없음을 의미합니다.

    코드가 컬렉션 탐색 순서에 영향을 받는 경우 컬렉션에서 노출하는 열거자 개체의 특징을 모르면 For Each...Next 루프를 사용하지 않는 것이 좋습니다. 이 경우 For...Next 또는 Do...Loop 등의 다른 루프 구조를 사용하면 좀더 신뢰할 수 있는 결과를 얻을 수 있습니다.

  • 컬렉션 수정. 일반적으로 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 루프로 배열을 여러 번 반복할 수 있음을 의미합니다. 그러나 배열 요소는 읽을 수만 있고 변경할 수는 없습니다. 자세한 내용은 방법: 컬렉션이나 배열의 각 요소에 대해 몇 가지 문 실행을 참조하십시오.

예제

다음 예제에서는 For Each...Next 문을 사용하여 컬렉션의 모든 요소에서 "Hello" 문자열을 검색합니다. thisCollection 컬렉션이 이미 만들어져 있고 해당 요소가 String 형식임을 가정합니다.

Dim found As Boolean = False
Dim thisCollection As New Collection
For Each thisObject As String In thisCollection
    If thisObject = "Hello" Then
        found = True
        Exit For
    End If
Next thisObject

참고 항목

작업

방법: 컬렉션이나 배열의 각 요소에 대해 몇 가지 문 실행

방법: 루프 성능 개선

개념

루프 구조

Visual Basic의 컬렉션

확대 변환과 축소 변환

참조

While...End While 문(Visual Basic)

Do...Loop 문(Visual Basic)

변경 기록

날짜

변경 내용

이유

2008년 7월

축소 변환에 대한 단원이 추가되었습니다.

고객 의견