다음을 통해 공유


반복기(Visual Basic)

반복기를 사용하여 목록 및 배열과 같은 컬렉션을 단계별로 실행할 수 있습니다.

반복기 메서드 또는 get 접근자는 컬렉션에 대해 사용자 지정 반복을 수행합니다. 반복기 메서드는 Yield 문을 사용하여 각 요소를 한 번에 하나씩 반환합니다. 명령문 Yield 에 도달하면 코드의 현재 위치가 저장됩니다. 다음에 반복기 함수가 호출될 때 해당 위치에서 실행이 다시 시작됩니다.

당신은 For Each…Next 문이나 LINQ 쿼리를 사용하여 클라이언트 코드에서 반복기를 사용합니다.

다음 예제에서 For Each 루프의 첫 번째 반복은 SomeNumbers 문에 도달할 때까지 Yield 반복기 메서드에서 실행을 진행시킵니다. 이 반복은 값 3을 반환하고 반복기 메서드의 현재 위치는 유지됩니다. 루프의 다음 반복에서 반복기 메서드의 실행은 중단된 위치에서 계속되며 문에 Yield 도달하면 다시 중지됩니다. 이 반복은 값 5를 반환하고 반복기 메서드의 현재 위치는 다시 유지됩니다. 반복기 메서드의 끝에 도달하면 루프가 완료됩니다.

Sub Main()
    For Each number As Integer In SomeNumbers()
        Console.Write(number & " ")
    Next
    ' Output: 3 5 8
    Console.ReadKey()
End Sub

Private Iterator Function SomeNumbers() As System.Collections.IEnumerable
    Yield 3
    Yield 5
    Yield 8
End Function

반복기 메서드 또는 get 접근자IEnumerable의 반환 형식은 IEnumerable<T>, IEnumerator, 또는 IEnumerator<T>일 수 있습니다.

Exit Function 문이나 Return 문을 사용하여 반복을 종료할 수 있습니다.

Visual Basic 반복기 함수 또는 get 접근자 선언에는 반복기 한정자가 포함됩니다.

반복기는 Visual Studio 2012의 Visual Basic에서 도입되었습니다.

비고

Simple Iterator 예제를 제외한 이 문서의 모든 예제에 대해서는 System.Collections 네임스페이스에 대한 System.Collections.Generic 문을 포함합니다.

단순 반복기

다음 예제에는 Yield 루프 내에 있는 단일 문이 포함되어 있습니다. 에서 Main문 본문의 For Each 각 반복은 다음 Yield 문으로 진행되는 반복기 함수에 대한 호출을 만듭니다.

Sub Main()
    For Each number As Integer In EvenSequence(5, 18)
        Console.Write(number & " ")
    Next
    ' Output: 6 8 10 12 14 16 18
    Console.ReadKey()
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 As Integer = firstNumber To lastNumber
        If number Mod 2 = 0 Then
            Yield number
        End If
    Next
End Function

컬렉션 클래스 만들기

다음 예제에서 DaysOfTheWeek 클래스는 IEnumerable 인터페이스를 구현하며, 이 인터페이스에는 GetEnumerator 메서드가 필요합니다. 컴파일러는 GetEnumerator 메서드를 암시적으로 호출하여 IEnumerator를 반환합니다.

메서드는 GetEnumerator 문을 사용하여 각 문자열을 Yield 한 번에 하나씩 반환하고 Iterator 한정자는 함수 선언에 있습니다.

Sub Main()
    Dim days As New DaysOfTheWeek()
    For Each day As String In days
        Console.Write(day & " ")
    Next
    ' Output: Sun Mon Tue Wed Thu Fri Sat
    Console.ReadKey()
End Sub

Private Class DaysOfTheWeek
    Implements IEnumerable

    Public days =
        New String() {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}

    Public Iterator Function GetEnumerator() As IEnumerator _
        Implements IEnumerable.GetEnumerator

        ' Yield each day of the week.
        For i As Integer = 0 To days.Length - 1
            Yield days(i)
        Next
    End Function
End Class

다음 예제에서는 동물 컬렉션을 포함하는 클래스를 만듭니다 Zoo .

클래스 인스턴스(For Each)를 참조하는 문은 theZoo 메서드를 암시적으로 호출합니다GetEnumerator. For Each 문은 BirdsMammals 속성을 참조하며 AnimalsForType로 명명된 반복기 메서드를 사용합니다.

Sub Main()
    Dim theZoo As New Zoo()

    theZoo.AddMammal("Whale")
    theZoo.AddMammal("Rhinoceros")
    theZoo.AddBird("Penguin")
    theZoo.AddBird("Warbler")

    For Each name As String In theZoo
        Console.Write(name & " ")
    Next
    Console.WriteLine()
    ' Output: Whale Rhinoceros Penguin Warbler

    For Each name As String In theZoo.Birds
        Console.Write(name & " ")
    Next
    Console.WriteLine()
    ' Output: Penguin Warbler

    For Each name As String In theZoo.Mammals
        Console.Write(name & " ")
    Next
    Console.WriteLine()
    ' Output: Whale Rhinoceros

    Console.ReadKey()
End Sub

Public Class Zoo
    Implements IEnumerable

    ' Private members.
    Private animals As New List(Of Animal)

    ' Public methods.
    Public Sub AddMammal(ByVal name As String)
        animals.Add(New Animal With {.Name = name, .Type = Animal.TypeEnum.Mammal})
    End Sub

    Public Sub AddBird(ByVal name As String)
        animals.Add(New Animal With {.Name = name, .Type = Animal.TypeEnum.Bird})
    End Sub

    Public Iterator Function GetEnumerator() As IEnumerator _
        Implements IEnumerable.GetEnumerator

        For Each theAnimal As Animal In animals
            Yield theAnimal.Name
        Next
    End Function

    ' Public members.
    Public ReadOnly Property Mammals As IEnumerable
        Get
            Return AnimalsForType(Animal.TypeEnum.Mammal)
        End Get
    End Property

    Public ReadOnly Property Birds As IEnumerable
        Get
            Return AnimalsForType(Animal.TypeEnum.Bird)
        End Get
    End Property

    ' Private methods.
    Private Iterator Function AnimalsForType( _
    ByVal type As Animal.TypeEnum) As IEnumerable
        For Each theAnimal As Animal In animals
            If (theAnimal.Type = type) Then
                Yield theAnimal.Name
            End If
        Next
    End Function

    ' Private class.
    Private Class Animal
        Public Enum TypeEnum
            Bird
            Mammal
        End Enum

        Public Property Name As String
        Public Property Type As TypeEnum
    End Class
End Class

Try 블록

Visual Basic에서는 YieldTry 블록에 문을 허용합니다. Try 문을 포함하는 블록에는 Yield 블록이 있을 수 있으며 Catch 블록을 가질 수 있습니다.

다음 예제에는 반복기 함수의 Try, Catch, 및 Finally 블록이 포함됩니다. 반복자 함수 내의 Finally 블록은 For Each 반복이 완료되기 전에 실행됩니다.

Sub Main()
    For Each number As Integer In Test()
        Console.WriteLine(number)
    Next
    Console.WriteLine("For Each is done.")

    ' Output:
    '  3
    '  4
    '  Something happened. Yields are done.
    '  Finally is called.
    '  For Each is done.
    Console.ReadKey()
End Sub

Private Iterator Function Test() As IEnumerable(Of Integer)
    Try
        Yield 3
        Yield 4
        Throw New Exception("Something happened. Yields are done.")
        Yield 5
        Yield 6
    Catch ex As Exception
        Console.WriteLine(ex.Message)
    Finally
        Console.WriteLine("Finally is called.")
    End Try
End Function

Yield 문은 Catch 블록 또는 Finally 블록 내에 있을 수 없습니다.

For Each 반복기 메서드 대신 본문에서 예외 Catch가 발생하면 반복기 함수에서 Catch 블록은 실행되지 않지만 블록은 실행됩니다. 반복기 함수 내의 Catch 블록은 반복기 함수 내에서 발생하는 예외만 catch합니다.

익명 메서드

Visual Basic에서 익명 함수는 반복기 함수일 수 있습니다. 다음 예제에서는 이를 보여 줍니다.

Dim iterateSequence = Iterator Function() _
                      As IEnumerable(Of Integer)
                          Yield 1
                          Yield 2
                      End Function

For Each number As Integer In iterateSequence()
    Console.Write(number & " ")
Next
' Output: 1 2
Console.ReadKey()

다음 예제에는 인수의 유효성을 검사하는 비 반복기 메서드가 있습니다. 이 메서드는 컬렉션 요소를 설명하는 익명 반복기의 결과를 반환합니다.

Sub Main()
    For Each number As Integer In GetSequence(5, 10)
        Console.Write(number & " ")
    Next
    ' Output: 5 6 7 8 9 10
    Console.ReadKey()
End Sub

Public Function GetSequence(ByVal low As Integer, ByVal high As Integer) _
As IEnumerable
    ' Validate the arguments.
    If low < 1 Then
        Throw New ArgumentException("low is too low")
    End If
    If high > 140 Then
        Throw New ArgumentException("high is too high")
    End If

    ' Return an anonymous iterator function.
    Dim iterateSequence = Iterator Function() As IEnumerable
                              For index = low To high
                                  Yield index
                              Next
                          End Function
    Return iterateSequence()
End Function

유효성 검사가 반복기 함수 내에 있는 경우 본문의 첫 번째 반복 For Each 이 시작될 때까지 유효성 검사를 수행할 수 없습니다.

제네릭 목록과 함께 이터레이터 사용

다음 예제에서 제네릭 클래스는 Stack(Of T) 제네릭 인터페이스를 IEnumerable<T> 구현합니다. 메서드는 Push 형식 T의 배열에 값을 할당합니다. 메서드는 GetEnumerator 문을 사용하여 배열 값을 반환합니다 Yield .

제네릭 메서드 외에도 제네릭 GetEnumeratorGetEnumerator 이 아닌 메서드도 구현해야 합니다. 이렇게 된 이유는 IEnumerable<T>이/가 IEnumerable로부터 상속받기 때문입니다. 비제네릭 구현은 제네릭 구현에 의존합니다.

이 예제에서는 명명된 반복기를 사용하여 동일한 데이터 컬렉션을 반복하는 다양한 방법을 지원합니다. 이러한 명명된 반복기는 TopToBottomBottomToTop 속성, 그리고 TopN 메서드입니다.

속성 선언에는 BottomToTop 키워드가 Iterator에 포함됩니다.

Sub Main()
    Dim theStack As New Stack(Of Integer)

    ' Add items to the stack.
    For number As Integer = 0 To 9
        theStack.Push(number)
    Next

    ' Retrieve items from the stack.
    ' For Each is allowed because theStack implements
    ' IEnumerable(Of Integer).
    For Each number As Integer In theStack
        Console.Write("{0} ", number)
    Next
    Console.WriteLine()
    ' Output: 9 8 7 6 5 4 3 2 1 0

    ' For Each is allowed, because theStack.TopToBottom
    ' returns IEnumerable(Of Integer).
    For Each number As Integer In theStack.TopToBottom
        Console.Write("{0} ", number)
    Next
    Console.WriteLine()
    ' Output: 9 8 7 6 5 4 3 2 1 0

    For Each number As Integer In theStack.BottomToTop
        Console.Write("{0} ", number)
    Next
    Console.WriteLine()
    ' Output: 0 1 2 3 4 5 6 7 8 9

    For Each number As Integer In theStack.TopN(7)
        Console.Write("{0} ", number)
    Next
    Console.WriteLine()
    ' Output: 9 8 7 6 5 4 3

    Console.ReadKey()
End Sub

Public Class Stack(Of T)
    Implements IEnumerable(Of T)

    Private values As T() = New T(99) {}
    Private top As Integer = 0

    Public Sub Push(ByVal t As T)
        values(top) = t
        top = top + 1
    End Sub

    Public Function Pop() As T
        top = top - 1
        Return values(top)
    End Function

    ' This function implements the GetEnumerator method. It allows
    ' an instance of the class to be used in a For Each statement.
    Public Iterator Function GetEnumerator() As IEnumerator(Of T) _
        Implements IEnumerable(Of T).GetEnumerator

        For index As Integer = top - 1 To 0 Step -1
            Yield values(index)
        Next
    End Function

    Public Iterator Function GetEnumerator1() As IEnumerator _
        Implements IEnumerable.GetEnumerator

        Yield GetEnumerator()
    End Function

    Public ReadOnly Property TopToBottom() As IEnumerable(Of T)
        Get
            Return Me
        End Get
    End Property

    Public ReadOnly Iterator Property BottomToTop As IEnumerable(Of T)
        Get
            For index As Integer = 0 To top - 1
                Yield values(index)
            Next
        End Get
    End Property

    Public Iterator Function TopN(ByVal itemsFromTop As Integer) _
        As IEnumerable(Of T)

        ' Return less than itemsFromTop if necessary.
        Dim startIndex As Integer =
            If(itemsFromTop >= top, 0, top - itemsFromTop)

        For index As Integer = top - 1 To startIndex Step -1
            Yield values(index)
        Next
    End Function
End Class

구문 정보

반복기는 메서드 또는 get 접근자로 사용될 수 있습니다. 이벤트, 인스턴스 생성자, 정적 생성자 또는 정적 소멸자에서는 반복기가 발생할 수 없습니다.

문에 있는 Yield 식 형식에서 반복기의 반환 형식으로 암시적 변환이 있어야 합니다.

Visual Basic에서는 반복기 메서드가 매개 변수를 가질 수 없습니다.

Visual Basic에서 "Yield"는 예약된 단어가 아니며 Iterator 메서드 또는 get 접근자에서 사용될 때에만 특별한 의미를 가집니다.

기술 구현

반복기를 메서드로 작성하지만 컴파일러는 해당 반복기를 중첩된 클래스( 사실상 상태 컴퓨터)로 변환합니다. 이 클래스는 클라이언트 코드의 루프가 계속되는 동안 For Each...Next 반복기의 위치를 추적합니다.

컴파일러가 수행하는 작업을 확인하려면 Ildasm.exe 도구를 사용하여 반복기 메서드에 대해 생성된 공통 중간 언어 코드를 볼 수 있습니다.

클래스 또는 구조체에 대한 반복기를 만들 때 전체 IEnumerator 인터페이스를 구현할 필요가 없습니다. 컴파일러가 반복기를 감지하면 Current 또는 MoveNext 인터페이스의 Dispose, IEnumerator, 및 IEnumerator<T> 메서드를 자동으로 생성합니다.

루프의 For Each…Next 연속된 반복(또는 직접 호출 IEnumerator.MoveNext)에서 다음 반복기 코드 본문은 이전 Yield 문 다음에 다시 시작됩니다. 그런 다음 반복기 본문의 끝에 도달하거나 Yield 또는 Exit Function 문을 만나면 다음 Return 문으로 계속 진행합니다.

반복기는 IEnumerator.Reset 메서드를 지원하지 않습니다. 처음부터 다시 반복하려면 새 반복기를 가져와야 합니다.

자세한 내용은 Visual Basic 언어 사양을 참조하세요.

반복기 사용

반복기를 사용하면 복잡한 코드를 사용하여 목록 시퀀스를 채워야 하는 경우 루프의 For Each 단순성을 유지할 수 있습니다. 이 작업은 다음을 수행하려는 경우에 유용할 수 있습니다.

  • 첫 번째 For Each 루프 반복 후 목록 시퀀스를 수정합니다.

  • For Each 루프의 첫 번째 반복 이전에 큰 목록을 완전히 로드하지 마세요. 예를 들어 테이블 행을 일괄적으로 불러오는 페이지를 나누어 가져오는 작업이 있습니다. 또 다른 예로 EnumerateFiles .NET Framework 내에서 반복기를 구현하는 메서드가 있습니다.

  • 반복기에서 목록 작성을 캡슐화합니다. 반복기 메서드에서 목록을 빌드한 다음 각 결과를 루프로 생성할 수 있습니다.

참고하십시오