反復子を使用して、リストや配列などのコレクションをステップ実行できます。
反復子メソッドまたは get
アクセサーは、コレクションに対してカスタムイテレーションを実行します。 反復子メソッドは Yield ステートメントを使用して、各要素を一度に 1 つずつ返します。
Yield
ステートメントに達すると、コード内の現在の場所が記憶されます。 次に反復子関数が呼び出されるときに、その場所から実行が再開されます。
For Each... を使用して、クライアント コードから反復子を使用 します。次 のステートメント、または 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
アクセサー宣言には、 Iterator 修飾子が含まれています。
反復子は Visual Studio 2012 の Visual Basic で導入されました。
注
単純反復子の例を除く記事のすべての例については、System.Collections
および System.Collections.Generic
名前空間の Imports ステートメントを含めます。
単純な反復子
次の例では、For..内に 1 つの 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
ステートメントを使用して各文字列を一度に 1 つずつ返し、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
プロパティとBirds
プロパティを参照するMammals
ステートメントでは、名前付き反復子メソッド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
ブロックを試す
Visual Basic では、Try の Try
ブロックでYield
ステートメントを使用できます。捕まえる。。。Finally ステートメント。
Yield
ステートメントを持つTry
ブロックには、Catch
ブロックを含め、Finally
ブロックを含めることができます。
次の例には、反復子関数の 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
ブロックは実行されませんが、反復子関数のFinally
ブロックが実行されます。 反復子関数内の 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
ステートメントを使用して配列値を返します。
ジェネリック GetEnumerator メソッドに加えて、非ジェネリック GetEnumerator メソッドも実装する必要があります。 これは、 IEnumerable<T> が IEnumerableから継承されるためです。 非ジェネリック実装は、ジェネリック実装に委ねます。
この例では、名前付き反復子を使用して、同じデータコレクションを反復処理するさまざまな方法をサポートしています。 これらの名前付き反復子は、 TopToBottom
プロパティと BottomToTop
プロパティ、および 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 では、反復子メソッドに ByRef
パラメーターを指定することはできません。
Visual Basic では、"Yield" は予約語ではなく、 Iterator
メソッドまたは get
アクセサーで使用される場合にのみ特別な意味を持ちます。
技術的な実装
反復子はメソッドとして記述しますが、コンパイラは、実際にはステート マシンである入れ子になったクラスに変換します。 このクラスは、クライアント コード内の For Each...Next
ループが継続する限り、反復子の位置を追跡します。
コンパイラの動作を確認するには、Ildasm.exe ツールを使用して、反復子メソッド用に生成された共通の中間言語コードを表示できます。
クラスまたは構造体の反復子を作成するときに、IEnumerator インターフェイス全体を実装する必要はありません。 コンパイラは、反復子を検出すると、Current
またはMoveNext
インターフェイスのDispose
、IEnumerator、およびIEnumerator<T>メソッドを自動的に生成します。
For Each…Next
ループの反復 (または IEnumerator.MoveNext
への直接呼び出し) のたびに、前のYield
ステートメントの後に次の反復子コード本体が再開されます。 次に、反復子本体の末尾に達するか、Exit Function
またはReturn
ステートメントが検出されるまで、次のYield
ステートメントに進みます。
反復子は、 IEnumerator.Reset メソッドをサポートしていません。 最初から再度反復処理するには、新しい反復子を取得する必要があります。
詳細については、「 Visual Basic 言語仕様」を参照してください。
反復子の使用
反復子を使用すると、複雑なコードを使用してリスト シーケンスを設定する必要がある場合に、 For Each
ループの簡略化を維持できます。 これは、次の操作を行う場合に役立ちます。
最初の
For Each
ループイテレーションの後にリスト シーケンスを変更します。For Each
ループの最初の反復処理の前に、大きなリストを完全に読み込まないようにします。 たとえば、一度にまとめてテーブル行を読み込むためのページ単位の取得が考えられます。 もう 1 つの例として、.NET Framework 内に反復子を実装する EnumerateFiles メソッドがあります。反復子内のリストの構築をカプセル化します。 反復子メソッドでは、リストを作成し、各結果をループに生成できます。
こちらも参照ください
.NET