Iteratorer (Visual Basic)
En iterator kan användas för att gå igenom samlingar som listor och matriser.
En iteratormetod eller get
-accessor utför en anpassad iteration över en samling. En iteratormetod använder yield-instruktionen för att returnera varje element ett i taget. När en Yield
instruktion har nåtts sparas den aktuella platsen i koden. Körningen startas om från den platsen nästa gång iteratorfunktionen anropas.
Du använder en iterator från klientkoden med hjälp av en För varje... Nästa instruktion, eller med hjälp av en LINQ-fråga.
I följande exempel gör den första iterationen av loopen att körningen For Each
SomeNumbers
fortsätter i iteratormetoden tills den första Yield
instruktionen har nåtts. Den här iterationen returnerar värdet 3 och den aktuella platsen i iteratormetoden behålls. Vid nästa iteration av loopen fortsätter körningen i iteratormetoden från där den slutade och stoppas igen när den når en Yield
-instruktion. Den här iterationen returnerar värdet 5 och den aktuella platsen i iteratormetoden behålls igen. Loopen slutförs när iteratormetodens slut har nåtts.
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
Returtypen för en iteratormetod eller get
-accessor kan vara IEnumerable, IEnumerable<T>, IEnumeratoreller IEnumerator<T>.
Du kan använda en Exit Function
-instruktion för Return
att avsluta iterationen.
En Visual Basic-iteratorfunktion eller get
åtkomstdeklaration innehåller en Iterator-modifierare .
Iteratorer introducerades i Visual Basic i Visual Studio 2012.
Kommentar
För alla exempel i artikeln förutom Simple Iterator-exemplet inkluderar du importinstruktioner för System.Collections
namnrymderna och System.Collections.Generic
.
Enkel iterator
I följande exempel finns en enda Yield
instruktion som finns i en For... Nästa loop. I Main
skapar varje iteration av instruktionstexten For Each
ett anrop till iteratorfunktionen, som fortsätter till nästa Yield
instruktion.
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
Skapa en samlingsklass
I följande exempel DaysOfTheWeek
implementerar IEnumerable klassen gränssnittet, som kräver en GetEnumerator metod. Kompilatorn anropar GetEnumerator
implicit metoden, som returnerar en IEnumerator.
Metoden GetEnumerator
returnerar varje sträng en i taget med hjälp av -instruktionen Yield
och en Iterator
modifierare finns i funktionsdeklarationen.
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
I följande exempel skapas en Zoo
klass som innehåller en samling djur.
Instruktionen For Each
som refererar till klassinstansen (theZoo
) anropar GetEnumerator
implicit metoden. De For Each
instruktioner som refererar till Birds
egenskaperna och Mammals
använder den namngivna AnimalsForType
iteratormetoden.
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
Prova block
Visual Basic tillåter en Yield
instruktion i Try
blocket för ett försök... Fånga... Slutligen -instruktion. Ett Try
block som har en Yield
-instruktion kan ha Catch
block och kan ha ett Finally
block.
I följande exempel ingår Try
, Catch
och Finally
block i en iteratorfunktion. Blocket Finally
i iteratorfunktionen körs innan iterationen For Each
är klar.
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
En Yield
instruktion får inte finnas i ett Catch
block eller ett Finally
block.
Om brödtexten For Each
(i stället för iteratormetoden) utlöser ett undantag körs inte ett Catch
block i iteratorfunktionen, men ett Finally
block i iteratorfunktionen körs. Ett Catch
block i en iteratorfunktion fångar bara upp undantag som inträffar i iteratorfunktionen.
Anonyma metoder
I Visual Basic kan en anonym funktion vara en iteratorfunktion. I följande exempel visas detta.
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()
I följande exempel finns en icke-iteratormetod som validerar argumenten. Metoden returnerar resultatet av en anonym iterator som beskriver samlingselementen.
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
Om valideringen i stället finns i iteratorfunktionen kan verifieringen inte utföras förrän den första iterationen av brödtexten har påbörjats For Each
.
Använda iteratorer med en allmän lista
I följande exempel implementerar den Stack(Of T)
generiska klassen det IEnumerable<T> generiska gränssnittet. Metoden Push
tilldelar värden till en matris av typen T
. Metoden GetEnumerator returnerar matrisvärdena med hjälp av -instruktionen Yield
.
Förutom den generiska GetEnumerator metoden måste även den icke-generiska GetEnumerator metoden implementeras. Det beror på att IEnumerable<T> ärver från IEnumerable. Den icke-generiska implementeringen skjuter upp den allmänna implementeringen.
I exemplet används namngivna iteratorer för att stödja olika sätt att iterera genom samma datainsamling. Dessa namngivna iteratorer är TopToBottom
egenskaperna och BottomToTop
och TopN
metoden.
Egenskapsdeklarationen BottomToTop
innehåller nyckelordet 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
Syntaxinformation
En iterator kan ske som en metod eller get
accessor. En iterator kan inte inträffa i en händelse, instanskonstruktor, statisk konstruktor eller statisk destructor.
En implicit konvertering måste finnas från uttryckstypen i -instruktionen Yield
till iteratorns returtyp.
I Visual Basic kan en iteratormetod inte ha några ByRef
parametrar.
I Visual Basic är "Yield" inte ett reserverat ord och har särskild betydelse endast när det används i en Iterator
metod eller get
accessor.
Teknisk implementering
Även om du skriver en iterator som en metod översätter kompilatorn den till en kapslad klass som i själva verket är en tillståndsdator. Den här klassen håller reda på iteratorns position så länge loopen For Each...Next
i klientkoden fortsätter.
Om du vill se vad kompilatorn gör kan du använda verktyget Ildasm.exe för att visa den gemensamma mellanliggande språkkoden som genereras för en iteratormetod.
När du skapar en iterator för en klass eller struct behöver du inte implementera hela IEnumerator gränssnittet. När kompilatorn identifierar iteratorn genereras Current
automatiskt metoderna IEnumerator , MoveNext
och och Dispose
i gränssnittet.IEnumerator<T>
På varje efterföljande iteration av loopen For Each…Next
(eller direktanropet till IEnumerator.MoveNext
) återupptas nästa iteratorkodtext efter föregående Yield
instruktion. Den fortsätter sedan till nästa Yield
-instruktion tills slutet av iteratortexten har nåtts, eller tills en instruktion eller Return
-Exit Function
instruktion påträffas.
Iteratorer stöder IEnumerator.Reset inte metoden. Om du vill iterera igen från början måste du skaffa en ny iterator.
Mer information finns i Visual Basic Language Specification.
Användning av iteratorer
Med iteratorer kan du upprätthålla enkelheten i en For Each
loop när du behöver använda komplex kod för att fylla i en listsekvens. Detta kan vara användbart när du vill göra följande:
Ändra listsekvensen efter den första
For Each
loop-iterationen.Undvik att helt läsa in en stor lista före den första iterationen av en
For Each
loop. Ett exempel är en sidhämtning för att läsa in en batch med tabellrader. Ett annat exempel är metoden EnumerateFiles som implementerar iteratorer i .NET Framework.Kapsla in att skapa listan i iteratorn. I iteratormetoden kan du skapa listan och sedan ge varje resultat i en loop.