|
Dit artikel is gepubliceerd op zondag 31 juli 2011 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.
In de Framework Class Library ( "FCL" ) is een Queue klasse voorzien om elementen van eender welke type ( elementtype Object ) aan toe te voegen en uit te verwijderen volgens het "FIFO" principe. Visual Basic 2010 Broncode Option Strict OnOption Explicit OnNamespace Example1 Class Client1 Public Shared Sub Main() Dim queue1 As New System.Collections. Queue Console.WriteLine(queue1.Count) queue1.Enqueue(1) queue1.Enqueue(2) queue1.Enqueue(3) Console.WriteLine(queue1.Count) Console.WriteLine(queue1.Peek()) queue1.Dequeue() Console.WriteLine(queue1.Count) Console.WriteLine(queue1.Peek()) queue1.Dequeue() Console.WriteLine(queue1.Count) Console.WriteLine(queue1.Peek()) queue1.Dequeue() Console.WriteLine(queue1.Count) queue1.Enqueue(1) queue1.Enqueue(2) queue1.Enqueue(3) Console.WriteLine(queue1.Count) Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output 0
3
1
2
2
1
3
0
3 Om één en ander te illustreren gaan we eens zelf een eenvoudige versie schrijven van een queue collectie : Visual Basic 2010 Broncode Namespace Example1 Class Queue Private m_Items As Object() = {} Public ReadOnly Property Count() As Integer Get Count = m_Items.Length End Get End Property Public Sub Enqueue( ByVal value As Object) ReDim Preserve m_Items(Count) m_Items(Count - 1) = value End Sub Public Function Peek() As Object Peek = m_Items(0) End Function Public Sub Dequeue() For index As Integer = 0 To Count - 2 m_Items(index) = m_Items(index + 1) Next ReDim Preserve m_Items(Count - 2) End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
De klasse Queue maakt intern gebruik van een Object() om de elementen in te verzamelen.
Zoals onderstaande Client demonstreert hebben we met onze nieuwe Queue objecten nu min of meer dezelfde mogelijkheden : Visual Basic 2010 Broncode Namespace Example1 Class Client2 Public Shared Sub Main() Dim queue1 As New Queue Console.WriteLine(queue1.Count) queue1.Enqueue(1) queue1.Enqueue(2) queue1.Enqueue(3) Console.WriteLine(queue1.Count) Console.WriteLine(queue1.Peek()) queue1.Dequeue() Console.WriteLine(queue1.Count) Console.WriteLine(queue1.Peek()) queue1.Dequeue() Console.WriteLine(queue1.Count) Console.WriteLine(queue1.Peek()) queue1.Dequeue() Console.WriteLine(queue1.Count) queue1.Enqueue(1) queue1.Enqueue(2) queue1.Enqueue(3) Console.WriteLine(queue1.Count) Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output 0
3
1
2
2
1
3
0
3 22.1.1. Vereiste Members voor het Enumereren van een CollectieWat niet met onze eigen Queue, maar wel met de reeds voorgedefinieerde Queue mogelijk is, is om op eenvoudige wijze over de elementen te itereren, om zo de elementen van deze collectie te benaderen ( ook wel "enumereren" genoemd ). Dit bijvoorbeeld via een For Each ... Next iteratie. Visual Basic 2010 Broncode Namespace Example1 Class Client3 Public Shared Sub Main() Dim queue1 As New System.Collections. Queue queue1.Enqueue(1) queue1.Enqueue(2) queue1.Enqueue(3) For Each element As Object In queue1 Console.Write(element.ToString() & " ") Next Console.WriteLine() For Each element As Object In queue1 Console.Write(element.ToString() & " ") Next Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output 1 2 3
1 2 3 Bovenstaande Client itereert twee maal over de elementen van de collectie, en benaderd deze elementen om ze op de console af te drukken.
In onze eigen Queue werd geen doorloopmechanisme voorzien, om op eenvoudige wijze over de elementen van collectie te enumereren, en zo de elementen te benaderen.
Als we toch dergelijk mechanisme wensen te creëren voor onze Queue objecten, dan zouden we dit kunnen doen door enkele extra members in de interface ( enkele extra publieke members ) van deze collectiestructuur te voorzien.
Bijvoorbeeld zou men in een client als volgt te werk kunnen gaan om over de elementen te enumereren : Visual Basic 2010 Broncode Namespace Example1 Class Client4 Public Shared Sub Main() Dim queue1 As New Queue queue1.Enqueue(1) queue1.Enqueue(2) queue1.Enqueue(3) Do While queue1.HasNext() queue1.MoveNext() Console.Write(queue1.Current.ToString() & " ") Loop queue1.Reset() Console.WriteLine() Do While queue1.HasNext() queue1.MoveNext() Console.Write(queue1.Current.ToString() & " ") Loop queue1.Reset() Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output 1 2 3
1 2 3 Waarbij : - HasNext aangeeft of er een volgend element is. - MoveNext ervoor zorgt dat de volgende keer Current weldegelijk het volgende element oplevert. - Current het huidige element oplevert. - Reset ervoor zorgt dat bij een volgende doorloping er opnieuw bij het begin van de collectie wordt begonnen. Dit is nodig om tussen twee doorloping door, de iteratielogica te kunnen "resetten".
Ook hier wordt dus twee maal over de elementen van de collectie geënumereerd, om de elementen op de console af te drukken.
Om onze Queue hierop te voorzien, kunnen we de klasse met deze members en een bijpassende implementatie uitbreiden : Visual Basic 2010 Broncode Namespace Example1 Partial Class Queue Private m_Cursor As Integer = -1 Public Function HasNext() As Boolean Dim cursorNextElement As Integer = m_Cursor + 1 HasNext = (cursorNextElement <= m_Items.GetUpperBound(0)) End Function Public Sub MoveNext() m_Cursor += 1 End Sub Public ReadOnly Property Current() As Object Get Current = m_Items(m_Cursor) End Get End Property Public Sub Reset() m_Cursor = -1 End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Hierbij zijn nu de nodige voorzieningen aangebracht om het mogelijk te maken op eenvoudige wijze over de elementen van de collectie te enumereren.
Ook in andere user defined collecties zouden we dezelfde members en implementatie kunnen opnemen, om op eenvoudige wijze over de elementen van de collectie te enumereren.
Hieronder is een eenvoudige LinkedList gecreëerd : Visual Basic 2010 Broncode Namespace Example1 Class LinkedList Private m_Sentinel As Node = New Node(0) Private m_Tail As Node = m_Sentinel Public Sub Add( ByVal value As Object) m_Tail.NextNode = New Node(value) m_Tail = m_Tail.NextNode End Sub Public Class Node Public NextNode As Node Public Value As Object Public Sub New( ByVal value As Object) Me.Value = value End Sub Public Overrides Function ToString() As String ToString = Value.ToString() End Function End Class End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Dezelfde members ( met gelijkaardige implementatie ) kunnen aan de publieke interface van LinkedList worden toegevoegd om het doorlopen van de elementen mogelijk te maken : Visual Basic 2010 Broncode Namespace Example1 Partial Class LinkedList Private m_Cursor As Node = m_Sentinel Public Function HasNext() As Boolean Dim cursorNextElement As Node = m_Cursor.NextNode HasNext = (cursorNextElement IsNot Nothing) End Function Public Sub MoveNext() m_Cursor = m_Cursor.NextNode End Sub Public ReadOnly Property Current() As Object Get Current = m_Cursor.Value End Get End Property Public Sub Reset() m_Cursor = m_Sentinel End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Nu is het ook met LinkedList objecten mogelijk om op eenvoudige wijze over de elementen van deze collectie te enumereren : Visual Basic 2010 Broncode Namespace Example1 Class Client5 Public Shared Sub Main() Dim linkedList1 As New LinkedList linkedList1.Add(1) linkedList1.Add(2) linkedList1.Add(3) Do While linkedList1.HasNext() linkedList1.MoveNext() Console.Write(linkedList1.Current.ToString() & " ") Loop linkedList1.Reset() Console.WriteLine() Do While linkedList1.HasNext() linkedList1.MoveNext() Console.Write(linkedList1.Current.ToString() & " ") Loop linkedList1.Reset() Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output 1 2 3
1 2 3 We hebben hier nu geïllustreerd hoe members als HasNext, MoveNext en Reset nuttige zijn in de publieke interface van elk collectietype waar we op eenvoudige wijze over de elementen willen enumereren. boven
22.1.2. Enumerable InterfaceWe kunnen hiervoor bijgevolg ook een interface voor definiëren. Het kan immers nuttig zijn om alle itereerbare collecties in dezelfde context te gaan gebruiken.
De interface zou er zo kunnen uitzien : Visual Basic 2010 Broncode Namespace Example2 Interface IEnumerable Function HasNext() As Boolean Sub MoveNext() ReadOnly Property Current() As Object Sub Reset() End InterfaceEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Een basisklasse, in tegenstelling tot een interface, had hier niet toepasselijk geweest, gezien elk collectie type een andere implementatie voor de doorloop members nodig heeft. Onze Queue en LinkedList gaan immers op totaal verschillende manier de members HasNext, MoveNext en Current implementeren.
Onze Queue en List kunnen nu deze interface implementeren, waardoor beide types garanderen een gedrag te definiëren voor het doorlopen van de elementen : Visual Basic 2010 Broncode Namespace Example2 Class Queue : Implements IEnumerable Private m_Items As Object() = {} Public ReadOnly Property Count() As Integer Get Count = m_Items.Length End Get End Property Public Sub Enqueue( ByVal value As Object) ReDim Preserve m_Items(Count) m_Items(Count - 1) = value End Sub Public Function Peek() As Object Peek = m_Items(0) End Function Public Sub Dequeue() For index As Integer = 0 To Count - 2 m_Items(index) = m_Items(index + 1) Next ReDim Preserve m_Items(Count - 2) End Sub Private m_Cursor As Integer = -1 Public Function HasNext() As Boolean _ Implements IEnumerable. HasNext Dim cursorNextElement As Integer = m_Cursor + 1 HasNext = (cursorNextElement <= m_Items.GetUpperBound(0)) End Function Public Sub MoveNext() Implements IEnumerable. MoveNext m_Cursor += 1 End Sub Public ReadOnly Property Current() As Object _ Implements IEnumerable. Current Get Current = m_Items(m_Cursor) End Get End Property Public Sub Reset() Implements IEnumerable. Reset m_Cursor = -1 End Sub End Class Class LinkedList : Implements IEnumerable Private m_Sentinel As Node = New Node(0) Private m_Tail As Node = m_Sentinel Public Sub Add( ByVal value As Object) m_Tail.NextNode = New Node(value) m_Tail = m_Tail.NextNode End Sub Public Class Node Public NextNode As Node Public Value As Object Public Sub New( ByVal value As Object) Me.Value = value End Sub Public Overrides Function ToString() As String ToString = Value.ToString() End Function End Class Private m_Cursor As Node = m_Sentinel Public Function HasNext() As Boolean _ Implements IEnumerable. HasNext Dim cursorNextElement As Node = m_Cursor.NextNode HasNext = (cursorNextElement IsNot Nothing) End Function Public Sub MoveNext() Implements IEnumerable. MoveNext m_Cursor = m_Cursor.NextNode End Sub Public ReadOnly Property Current() As Object _ Implements IEnumerable. Current Get Current = m_Cursor.Value End Get End Property Public Sub Reset() Implements IEnumerable. Reset m_Cursor = m_Sentinel End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Alle types die IEnumerable implementeren kunnen nu in dezelfde context worden gebruikt. Een method als onderstaande Print zal de elementen afdrukken van de IEnumerable argumentwaarde : Visual Basic 2010 Broncode Namespace Example2 Class Client1 Public Shared Sub Main() Dim queue1 As New Queue queue1.Enqueue(1) queue1.Enqueue(2) queue1.Enqueue(3) Print(queue1) Print(queue1) Dim linkedList1 As New LinkedList linkedList1.Add(1) linkedList1.Add(2) linkedList1.Add(3) Print(linkedList1) Print(linkedList1) Console.ReadLine() End Sub Public Shared Sub Print( ByVal aCollection As IEnumerable) Do While aCollection.HasNext() aCollection.MoveNext() Console.Write(aCollection.Current.ToString() & " ") Loop aCollection.Reset() Console.WriteLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output 1 2 3
1 2 3
1 2 3
1 2 3 Toch zijn we nog altijd beperkt in de iteratie mogelijkheden die onze iteratiemembers ( uit de IEnumerable interface ) ons opleveren. boven
22.1.3. EnumeratorStel in onderstaande Client levert de functie GetQueueAverage het gemiddelde op. Deze GetQueueAverage wordt gebruikt in onze PrintQueue om niet alleen de elementwaarde die benaderd wordt in de iteratie af te drukken, maar ook af te drukken hoe deze elementwaarde zich positioneert ten aanzien van het gemiddelde ( "> average", "< average" of "= average" ).
Als je deze onderstaande Client bekijkt dan zul je daar waarschijnlijk niet meteen een probleem in zien. Toch loopt het fout bij de uitvoer ( IndexOutOfRangeException ) : Visual Basic 2010 Broncode Namespace Example2 Class Client2 Public Shared Sub Main() Dim queue1 As New Queue queue1.Enqueue(1) queue1.Enqueue(2) queue1.Enqueue(3) PrintQueue(queue1) Console.ReadLine() End Sub Public Shared Sub PrintQueue( ByVal aQueue As Queue) Do While aQueue.HasNext() aQueue.MoveNext() Console.Write(aQueue.Current.ToString() & " ") Dim average As Integer = GetQueueAverage(aQueue) Select Case Convert.ToInt32(aQueue.Current()) Case Is > average Console.Write( "( > " & average & " ) ") Case Is < average Console.Write( "( < " & average & " ) ") Case Is = average Console.Write( "( = " & average & " ) ") End Select Loop aQueue.Reset() Console.WriteLine() End Sub Public Shared Function GetQueueAverage( ByVal aQueue As Queue) As Integer Dim counter As Integer Do While aQueue.HasNext() aQueue.MoveNext() GetQueueAverage += Convert.ToInt32(aQueue.Current) counter += 1 Loop aQueue.Reset() GetQueueAverage = Convert.ToInt32(GetQueueAverage / counter) End Function End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Het probleem zit hier in het feit dat we tijdens het enumereren over de elementen om ze af te drukken, ook nog eens over enumereren om het gemiddelde te bepalen. Maar beide doorlopingen werken met dezelfde "cursor", waardoor onze doorlopingen elkaar beïnvloeden en een foutief resultaat veroorzaken.
De essentie van het probleem is dat het collectie object zelf, maar over één cursor beschikt, en dus nooit meer dan een doorloping correct op hetzelfde moment kan laten uitvoeren.
Dit oplossen kan door de doorlooplogica ( inclusief alle members en hun implementaties ) uit de collectie te halen, en in een aparte "enumerator" onder te brengen. Als we beschikken over dergelijke enumerator ( met daarin de nodige members voorzien om over de collectie te enumereren ), die zelf als toestand bijhoudt welke de huidige "cursor" positie is, zullen verschillende doorlopingen ( die op verschillende enumerators gebeuren ) elkaar niet meer beïnvloeden.
Gezien de enumerator dan afgescheiden wordt van de collectie ( waarover we moeten kunnen enumereren ) moet bij elke doorloping ( die op de collectie moet gebeuren ) eerst de enumerator van die collectie worden opgevraagd.
Een constructie voor een enumereerbare Queue zou er dan als volgt kunnen uitzien : Visual Basic 2010 Broncode Namespace Example3 Class Queue Private m_Items As Object() = {} Public ReadOnly Property Count() As Integer Get Count = m_Items.Length End Get End Property Public Sub Enqueue( ByVal value As Object) ReDim Preserve m_Items(Count) m_Items(Count - 1) = value End Sub Public Function Peek() As Object Peek = m_Items(0) End Function Public Sub Dequeue() For index As Integer = 0 To Count - 2 m_Items(index) = m_Items(index + 1) Next ReDim Preserve m_Items(Count - 2) End Sub Public Function GetEnumerator() As QueueEnumerator GetEnumerator = New QueueEnumerator(m_Items) End Function End Class Class QueueEnumerator Private m_Items As Object() = {} Private m_Cursor As Integer = -1 Public Sub New( ByVal items As Object()) m_Items = items End Sub Public Function HasNext() As Boolean Dim cursorNextElement As Integer = m_Cursor + 1 HasNext = (cursorNextElement <= m_Items.GetUpperBound(0)) End Function Public Sub MoveNext() m_Cursor += 1 End Sub Public ReadOnly Property Current() As Object Get Current = m_Items(m_Cursor) End Get End Property Public Sub Reset() m_Cursor = -1 End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Aan de constructor van de QueueEnumerator wordt de referentie doorgegeven van de interne Object() van de Queue collectie zelf. Ook de iterator moet immers beschikken over de interne collectie, anders kan bijvoorbeeld de member Current nooit het huidige element uit die interne collectie opleveren.
In onderstaande client gaan we nu opnieuw ( net zoals in vorig voorbeelden ) verschillende doorloping op het zelfde moment laten uitvoeren : Visual Basic 2010 Broncode Namespace Example3 Class Client Public Shared Sub Main() Dim queue1 As New Queue queue1.Enqueue(1) queue1.Enqueue(2) queue1.Enqueue(3) PrintQueue(queue1) Console.ReadLine() End Sub Public Shared Sub PrintQueue( ByVal aQueue As Queue) Dim aQueueEnumerator As QueueEnumerator = aQueue.GetEnumerator() Do While aQueueEnumerator.HasNext() aQueueEnumerator.MoveNext() Console.Write(aQueueEnumerator.Current.ToString() & " ") Dim average As Integer = GetQueueAverage(aQueue) Select Case Convert.ToInt32(aQueueEnumerator.Current()) Case Is > average Console.Write( "( > " & average & " ) ") Case Is < average Console.Write( "( < " & average & " ) ") Case Is = average Console.Write( "( = " & average & " ) ") End Select Loop aQueueEnumerator.Reset() Console.WriteLine() End Sub Public Shared Function GetQueueAverage( ByVal aQueue As Queue) As Integer Dim aQueueEnumerator As QueueEnumerator = aQueue.GetEnumerator() Dim counter As Integer Do While aQueueEnumerator.HasNext() aQueueEnumerator.MoveNext() GetQueueAverage += Convert.ToInt32(aQueueEnumerator.Current) counter += 1 Loop aQueueEnumerator.Reset() GetQueueAverage = Convert.ToInt32(GetQueueAverage / counter) End Function End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output 1 ( < 2 ) 2 ( = 2 ) 3 ( > 2 ) Deze keer gebeuren de doorlopingen op een door de collectie opgeleverde enumerator, in plaats van op de collectie zelf. Elke enumerator ( en dus ook elke doorloping ) kan hier zelf zijn eigen "cursor" beheren. De verschillende doorlopingen worden dus niet door elkaar beïnvloed.
Alles wat enumereerbaar is moet nu niet meer rechtstreeks beschikken over de members HasNext, MoveNext, Current en Reset, maar moet beschikken over een enumerator : Visual Basic 2010 Broncode Namespace Example4 Interface IEnumerable Function GetEnumerator() As IEnumerator End Interface Interface IEnumerator Function HasNext() As Boolean Sub MoveNext() ReadOnly Property Current() As Object Sub Reset() End InterfaceEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Onderstaand voorbeeld illustreert hoe Queue en LinkedList de interface IEnumerable kunnen implementeren : Visual Basic 2010 Broncode Namespace Example4 Class Queue : Implements IEnumerable Private m_Items As Object() = {} Public ReadOnly Property Count() As Integer Get Count = m_Items.Length End Get End Property Public Sub Enqueue( ByVal value As Object) ReDim Preserve m_Items(Count) m_Items(Count - 1) = value End Sub Public Function Peek() As Object Peek = m_Items(0) End Function Public Sub Dequeue() For index As Integer = 0 To Count - 2 m_Items(index) = m_Items(index + 1) Next ReDim Preserve m_Items(Count - 2) End Sub Public Function GetEnumerator() As IEnumerator Implements IEnumerable. GetEnumerator GetEnumerator = New QueueEnumerator(m_Items) End Function End Class Class QueueEnumerator : Implements IEnumerator Private m_Items As Object() Private m_Cursor As Integer = -1 Public Sub New( ByVal items As Object()) m_Items = items End Sub Public Function HasNext() As Boolean Implements IEnumerator. HasNext Dim cursorNextElement As Integer = m_Cursor + 1 HasNext = (cursorNextElement <= m_Items.GetUpperBound(0)) End Function Public Sub MoveNext() Implements IEnumerator. MoveNext m_Cursor += 1 End Sub Public ReadOnly Property Current() As Object Implements IEnumerator. Current Get Current = m_Items(m_Cursor) End Get End Property Public Sub Reset() Implements IEnumerator. Reset m_Cursor = -1 End Sub End Class Class LinkedList : Implements IEnumerable Private m_Sentinel As Node = New Node(0) Private m_Tail As Node = m_Sentinel Public Sub Add( ByVal value As Object) m_Tail.NextNode = New Node(value) m_Tail = m_Tail.NextNode End Sub Public Class Node Public NextNode As Node Public Value As Object Public Sub New( ByVal value As Object) Me.Value = value End Sub Public Overrides Function ToString() As String ToString = Value.ToString() End Function End Class Public Function GetEnumerator() As IEnumerator _ Implements IEnumerable. GetEnumerator GetEnumerator = New LinkedListEnumerator(m_Sentinel) End Function End Class Class LinkedListEnumerator : Implements IEnumerator Private m_Sentinel As LinkedList. Node Private m_Cursor As LinkedList. Node Public Sub New( ByVal sentinel As LinkedList. Node) m_Sentinel = sentinel m_Cursor = m_Sentinel End Sub Public Function HasNext() As Boolean Implements IEnumerator. HasNext Dim cursorNextElement As LinkedList. Node = m_Cursor.NextNode HasNext = (cursorNextElement IsNot Nothing) End Function Public Sub MoveNext() Implements IEnumerator. MoveNext m_Cursor = m_Cursor.NextNode End Sub Public ReadOnly Property Current() As Object Implements IEnumerator. Current Get Current = m_Cursor.Value End Get End Property Public Sub Reset() Implements IEnumerator. Reset m_Cursor = m_Sentinel End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
In onderstaand Client kunnen we nu op eenvoudige wijze over de elementen van een LinkedList of Queue object enumereren : Visual Basic 2010 Broncode Namespace Example4 Class Client1 Public Shared Sub Main() Dim queue1 As New Queue queue1.Enqueue(1) queue1.Enqueue(2) queue1.Enqueue(3) Print(queue1) Print(queue1) Dim linkedList1 As New LinkedList linkedList1.Add(1) linkedList1.Add(2) linkedList1.Add(3) Print(linkedList1) Print(linkedList1) Console.ReadLine() End Sub Public Shared Sub Print( ByVal aCollection As IEnumerable) Dim aEnumerator As IEnumerator = aCollection.GetEnumerator() Do While aEnumerator.HasNext() aEnumerator.MoveNext() Console.Write(aEnumerator.Current.ToString() & " ") Loop aEnumerator.Reset() Console.WriteLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output 1 2 3
1 2 3
1 2 3
1 2 3 boven
22.1.4. System.Collections.IEnumerable en IEnumeratorInterfaces als IEnumerable en IEnumerator zoals wij ze in vorige voorbeelden hadden gedefinieerd : Interface IEnumerable Function GetEnumerator() As IEnumerator End Interface Interface IEnumerator Function HasNext() As Boolean Sub MoveNext() ReadOnly Property Current() As Object Sub Reset() End Interface Hadden we eigenlijk niet hoeven creëren, in de System.Collections namespace bevinden zich reeds vergelijkbare interfaces : Interface IEnumerable Function GetEnumerator() As IEnumerator End Interface Interface IEnumerator Function MoveNext() As Boolean ReadOnly Property Current() As Object Sub Reset() End Interface Het enigste verschil is dat de implementatie van MoveNext bij deze voorgedefinieerde interface zowel naar het volgende element navigeert ( wat onze MoveNext deed ), als aangeeft of er nog wel een volgend element is ( wat onze HasNext deed ).
In plaats van zelf interfaces hiervoor te definiëren, kunnen we beter de hiervoor reeds voorziene interface gebruiken.
Laten we eens onze user-defined Persons collectie enumereerbaar maken : Visual Basic 2010 Broncode Namespace Example5 Class Person Private m_Name As String Public Sub New( ByVal name As String) Me.Name = name End Sub Public Property Name() As String Get Name = m_Name End Get Set( ByVal value As String) m_Name = value End Set End Property Public Overrides Function ToString() As String ToString = Name End Function End Class Class Persons : Implements System.Collections. IEnumerable Private m_Items As Person() = {} Public ReadOnly Property Count() As Integer Get Count = m_Items.GetLength(0) End Get End Property Public Sub Add( ByVal item As Person) ReDim Preserve m_Items(Count) m_Items(Count - 1) = item End Sub Public Function GetEnumerator() As System.Collections. IEnumerator _ Implements System.Collections.IEnumerable. GetEnumerator GetEnumerator = New PersonsEnumerator(m_Items) End Function Private Class PersonsEnumerator Implements System.Collections. IEnumerator Private m_Items As Person() Private m_Cursor As Integer = -1 Public Sub New( ByVal items As Person()) m_Items = items End Sub Public Function MoveNext() As Boolean _ Implements System.Collections.IEnumerator. MoveNext m_Cursor += 1 MoveNext = (m_Cursor <= m_Items.GetUpperBound(0)) End Function Public ReadOnly Property Current() As Object _ Implements System.Collections.IEnumerator. Current Get Current = m_Items(m_Cursor) End Get End Property Public Sub Reset() Implements System.Collections.IEnumerator. Reset m_Cursor = -1 End Sub End Class End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Zoals onderstaande Client demonstreert is het nu mogelijk om over de elementen van Persons object te enumereren : Visual Basic 2010 Broncode Namespace Example5 Class Client1 Public Shared Sub Main() Dim persons1 As New Persons persons1.Add( New Person( "John")) persons1.Add( New Person( "Jane")) Dim aEnumerator As IEnumerator = persons1.GetEnumerator() Do While aEnumerator.MoveNext() Console.Write(aEnumerator.Current.ToString() & " ") Loop aEnumerator.Reset() Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output John Jane De PersonsEnumerator klasse mag ( maar hoeft niet ) ingekapseld worden in de klasse Persons. Clients van Persons hoeven geen rechtstreekse toegang te hebben tot tot deze enumerator. De clients zullen voor het enumereren over de elementen van een Persons object immers enkel gebruik maken van de opgeleverde IEnumerator.
In plaats van manueel de enumerator van de collectie op te vragen, en dan manueel de IEnumerator members te gebruiken om over de enumerator te enumereren, kunnen we dit werk ook overlaten aan de For Each ... Next iteratie, die op de achtergrond toch zo vertaald wordt. Visual Basic 2010 Broncode Namespace Example5 Class Client2 Public Shared Sub Main() Dim persons1 As New Persons persons1.Add( New Person( "John")) persons1.Add( New Person( "Jane")) For Each element As Person In persons1 Console.Write(element.ToString() & " ") Next Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output John Jane Visual Studio : Plaats een breakpoint op regel (1) en voer de code uit, als je stap per stap verder gaat, zul je zien hoe deze For eigenlijk gebruik maakt van de members van de IEnumerable en IEnumerator implementaties. De For Each ... Next vereist dus weliswaar dat je een IEnumerable ( een "enumereerbare" ) collectie in de In clausule vermeldt.
De implementatie van de GetEnumerator() functie uit het interfacetype IEnumerable voor de klasse Persons kan nog vereenvoudigd worden.
De interne opslagstructuur die gebruikt wordt in Persons is een Person(). Wat we ondertussen ook weten is dat het type Array waarvan Person() is afgeleid reeds IEnumerable is, en deze dus reeds over een IEnumerator beschikt.
In plaats van zelf een IEnumerator type te definiëren kunnen we nu ook simpelweg de enumerator van onze interne array opleveren. Visual Basic 2010 Broncode Namespace Example6 Class Persons : Implements System.Collections. IEnumerable Private m_Items As Example5. Person() = {} Public ReadOnly Property Count() As Integer Get Count = m_Items.GetLength(0) End Get End Property Public Sub Add( ByVal item As Example5. Person) ReDim Preserve m_Items(Count) m_Items(Count - 1) = item End Sub Default Public Property Item( ByVal index As Integer) As Example5. Person Get Item = m_Items(index) End Get Set( ByVal value As Example5. Person) m_Items(index) = value End Set End Property Public Function GetEnumerator() As System.Collections. IEnumerator _ Implements System.Collections.IEnumerable. GetEnumerator GetEnumerator = m_Items.GetEnumerator() End Function End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Een eigen IEnumerator implementatie zoals PersonsEnumerator is hier nu niet meer noodzakelijk. Visual Basic 2010 Broncode Namespace Example6 Class Client Public Shared Sub Main() Dim persons1 As New Persons persons1.Add( New Example5. Person( "John")) persons1.Add( New Example5. Person( "Jane")) For Each element As Example5. Person In persons1 Console.Write(element.ToString() & " ") Next Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output John Jane Ook indien we onze Persons collectie intern van een ArrayList laten gebruikmaken, kunnen we in de IEnumerable.GetEnumerator implementatie de enumerator van deze Arraylist opleveren : Visual Basic 2010 Broncode Namespace Example7 Class Persons : Implements System.Collections. IEnumerable Private m_Items As New ArrayList Public ReadOnly Property Count() As Integer Get Count = m_Items.Count End Get End Property Public Sub Add( ByVal item As Example5. Person) m_Items.Add(item) End Sub Default Public Property Item( ByVal index As Integer) As Example5. Person Get Item = DirectCast(m_Items.Item(index), Example5. Person) End Get Set( ByVal value As Example5. Person) m_Items.Item(index) = value End Set End Property Public Function GetEnumerator() As System.Collections. IEnumerator _ Implements System.Collections.IEnumerable. GetEnumerator GetEnumerator = m_Items.GetEnumerator() End Function End Class Class Client Public Shared Sub Main() Dim persons1 As New Persons persons1.Add( New Example5. Person( "John")) persons1.Add( New Example5. Person( "Jane")) For Each element As Example5. Person In persons1 Console.Write(element.ToString() & " ") Next Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output John Jane boven
22.1.5. OefeningOpgave :
Creëer een BoundedStack type waarbij de elementen worden toegevoegd en verwijderd volgens het stack principe, maar waarbij het aantal elementen tot een bepaalde capaciteit begrenst is.
Maak deze BoundedStack IEnumerable en creëer ook een eigen IEnumerator.
Oplossing : Visual Basic 2010 Broncode Namespace Exercise Class BoundedStack : Implements IEnumerable Protected m_Items As Object() Protected m_Count As Integer Public Sub New( ByVal capacity As Integer) ReDim m_Items(capacity - 1) End Sub Public ReadOnly Property Count() As Integer Get Count = m_Count End Get End Property Public ReadOnly Property Capacity() As Integer Get Capacity = m_Items.Length End Get End Property Public Sub Push( ByVal value As Object) If Count < Capacity Then m_Items(Count) = value m_Count += 1 End If End Sub Public Function Peek() As Object If Count > 0 Then Peek = m_Items(Count - 1) End Function Public Sub Pop() If Count > 0 Then m_Items(Count - 1) = 0 m_Count -= 1 End If End Sub Public Function GetEnumerator() As System.Collections. IEnumerator _ Implements System.Collections.IEnumerable. GetEnumerator GetEnumerator = New BoundedStackEnumerator(m_Items, Count) End Function Private Class BoundedStackEnumerator : Implements IEnumerator Private m_Items As Object() Private m_Count As Integer Private m_Cursor As Integer Public Sub New( ByVal items As Object(), ByVal count As Integer) m_Items = items m_Count = count m_Cursor = m_Count End Sub Public Function MoveNext() As Boolean _ Implements System.Collections.IEnumerator. MoveNext m_Cursor -= 1 MoveNext = (m_Cursor >= 0) End Function Public ReadOnly Property Current() As Object _ Implements System.Collections.IEnumerator. Current Get Current = m_Items(m_Cursor) End Get End Property Public Sub Reset() Implements System.Collections.IEnumerator. Reset m_Cursor = m_Count End Sub End Class End Class Class Client Public Shared Sub Main() Dim boundedStack1 As BoundedStack Dim enumerator As IEnumerator boundedStack1 = New BoundedStack(3) enumerator = boundedStack1.GetEnumerator() Console.WriteLine(boundedStack1.Count = 0) Console.WriteLine(boundedStack1.Capacity = 3) Console.WriteLine(boundedStack1.Peek() Is Nothing) Console.WriteLine(enumerator.MoveNext() = False) Console.WriteLine() boundedStack1.Pop() enumerator = boundedStack1.GetEnumerator() Console.WriteLine(boundedStack1.Count = 0) Console.WriteLine(boundedStack1.Capacity = 3) Console.WriteLine(boundedStack1.Peek() Is Nothing) Console.WriteLine(enumerator.MoveNext() = False) Console.WriteLine() boundedStack1.Push(10) enumerator = boundedStack1.GetEnumerator() Console.WriteLine(boundedStack1.Count = 1) Console.WriteLine(boundedStack1.Capacity = 3) Console.WriteLine(Convert.ToInt32(boundedStack1.Peek()) = 10) Console.WriteLine(enumerator.MoveNext() = True) Console.WriteLine(Convert.ToInt32(enumerator.Current) = 10) Console.WriteLine(enumerator.MoveNext() = False) Console.WriteLine() boundedStack1.Push(20) boundedStack1.Push(30) boundedStack1.Push(40) enumerator = boundedStack1.GetEnumerator() Console.WriteLine(boundedStack1.Count = 3) Console.WriteLine(boundedStack1.Capacity = 3) Console.WriteLine(Convert.ToInt32(boundedStack1.Peek()) = 30) Console.WriteLine(enumerator.MoveNext() = True) Console.WriteLine(Convert.ToInt32(enumerator.Current) = 30) Console.WriteLine(enumerator.MoveNext() = True) Console.WriteLine(Convert.ToInt32(enumerator.Current) = 20) Console.WriteLine(enumerator.MoveNext() = True) Console.WriteLine(Convert.ToInt32(enumerator.Current) = 10) Console.WriteLine(enumerator.MoveNext() = False) Console.WriteLine() boundedStack1.Pop() enumerator = boundedStack1.GetEnumerator() Console.WriteLine(boundedStack1.Count = 2) Console.WriteLine(boundedStack1.Capacity = 3) Console.WriteLine(Convert.ToInt32(boundedStack1.Peek()) = 20) Console.WriteLine(enumerator.MoveNext() = True) Console.WriteLine(Convert.ToInt32(enumerator.Current) = 20) Console.WriteLine(enumerator.MoveNext() = True) Console.WriteLine(Convert.ToInt32(enumerator.Current) = 10) Console.WriteLine(enumerator.MoveNext() = False) Console.WriteLine() Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True Bij wijze van "defensief programmeren" gaan de Peek en Pop implementaties eerst na of we wel over voldoende elementen beschikken ( If Count > 0 ). boven
22.1.6. Iterator Pattern - Robuuste IteratorOnderstaand voorbeeld bevat een BoundedQueue klasse, waarbij het aantal elementen begrenst is tot een bepaalde capaciteit. Visual Basic 2010 Broncode Namespace Example8 Class BoundedQueue : Implements IEnumerable Protected m_Items As Object() Protected m_Count As Integer Public Sub New( ByVal capacity As Integer) ReDim m_Items(capacity - 1) End Sub Public ReadOnly Property Count() As Integer Get Count = m_Count End Get End Property Public ReadOnly Property Capacity() As Integer Get Capacity = m_Items.Length End Get End Property Public Sub Enqueue( ByVal value As Object) If Count < Capacity Then m_Items(Count) = value m_Count += 1 End If End Sub Public Function Peek() As Object Peek = m_Items(0) End Function Public Sub Dequeue() For index As Integer = 0 To Count - 2 m_Items(index) = m_Items(index + 1) Next m_Items(Count - 1) = 0 m_Count -= 1 End Sub Public Overridable Function GetEnumerator() As IEnumerator _ Implements IEnumerable. GetEnumerator GetEnumerator = New BoundedQueueEnumerator(m_Items, Count) End Function Class BoundedQueueEnumerator : Implements IEnumerator Private m_Items As Object() Private m_Count As Integer Private m_Cursor As Integer = -1 Public Sub New( ByVal items As Object(), ByVal count As Integer) m_Items = items m_Count = count End Sub Public Function MoveNext() As Boolean _ Implements IEnumerator. MoveNext m_Cursor += 1 MoveNext = (m_Cursor < m_Count) End Function Public ReadOnly Property Current() As Object _ Implements IEnumerator. Current Get Current = m_Items(m_Cursor) End Get End Property Public Sub Reset() Implements IEnumerator. Reset m_Cursor = -1 End Sub End Class End Class Class Client1 Public Shared BoundedQueue1 As BoundedQueue Public Shared Sub Main() BoundedQueue1 = New BoundedQueue(2) PrintBoundedQueue1() BoundedQueue1.Enqueue(1) BoundedQueue1.Enqueue(2) BoundedQueue1.Enqueue(3) PrintBoundedQueue1() Console.ReadLine() End Sub Public Shared Sub PrintBoundedQueue1() Console.Write( "boundedQueue1 : ") For Each element As Object In BoundedQueue1 Console.Write(element.ToString() & " ") Next Console.WriteLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output boundedQueue1 :
boundedQueue1 : 1 2 Het toevoegen van een derde element zal - gezien de queue "bounded" is - bij capaciteit twee genegeerd worden (2).
Bemerk hoe de enumerator hier nu ook de informatie van het aantal elementen nodig heeft (1) om te vermijden dat men over alle elementen van de interne Object() gaat enumereren. Dit ook meteen de reden waarom we hier zelf een enumerator moeten definiëren, we kunnen immers in de IEnumerable.GetEnumerator implementatie niet zomaar de enumerator van de interne Object() ( m_Items.GetEnumerator() ) opleveren. Deze zou toelaten over alle elementen van deze array te enumereren.
Het "iteratorpattern" ( pattern dat definieert hoe enumerators moeten functioneren ) stelt dat een robuuste iterator ( enumerator ) een "snapshot" zou moeten zijn van de collectie. Als een collectie wordt gewijzigd ( bijvoorbeeld een element toegevoegd of verwijderd ) terwijl reeds een enumerator werd opgevraagd, dan zou dit geen effect mogen hebben op deze enumerator.
Toch zou dit hier het geval zijn, in onderstaand voorbeeld wordt dit geïllustreerd : Visual Basic 2010 Broncode Namespace Example8 Class Client2 Public Shared Sub Main() Client1.BoundedQueue1 = New BoundedQueue(5) Client1.BoundedQueue1.Enqueue(1) Client1.BoundedQueue1.Enqueue(2) Client1.BoundedQueue1.Enqueue(3) Client1.PrintBoundedQueue1() Console.Write( "boundedQueue1 : ") Dim elementCounter As Integer For Each element As Object In Client1.BoundedQueue1 elementCounter += 1 Console.Write(element.ToString() & " ") If elementCounter = 2 Then Client1.BoundedQueue1.Dequeue() End If Next Console.WriteLine() Client1.PrintBoundedQueue1() Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output boundedQueue1 : 1 2 3
boundedQueue1 : 1 2 0
boundedQueue1 : 2 3 Bij het verwijderen van een element (2) tijdens het enumereren over de elementen van deze collectie (1) levert de enumerator het foutieve resultaat ( "1 2 0" ) op. Volgens het iteratorpattern zou deze deze enumerator moeten werken met een snapshot van de toestand van de collectie op het moment dat deze enumerator wordt opgevraagd. Het resultaat had hier "1 2 3" moeten zijn.
Het wijzigen van de originele collectie heeft effect op de enumerator die reeds voor de wijziging werd opgevraagd. De enumerator werkt immers met dezelfde interne array instantie als de collectie zelf.
Dit probleem valt eenvoudig op te lossen door de enumerator met een kopie van de interne array van de collectie te laten werken : Visual Basic 2010 Broncode Namespace Example8 Class BetterBoundedQueue : Inherits BoundedQueue Public Sub New( ByVal capacity As Integer) MyBase.New(capacity) End Sub Public Overrides Function GetEnumerator() As IEnumerator Dim itemsClone As Object() = DirectCast(m_Items.Clone(), Object()) GetEnumerator = New BoundedQueueEnumerator(itemsClone, Count) End Function End Class Class Client3 Public Shared Sub Main() Client1.BoundedQueue1 = New BetterBoundedQueue(5) Client1.BoundedQueue1.Enqueue(1) Client1.BoundedQueue1.Enqueue(2) Client1.BoundedQueue1.Enqueue(3) Client1.PrintBoundedQueue1() Console.Write( "boundedQueue1 : ") Dim elementCounter As Integer For Each element As Object In Client1.BoundedQueue1 elementCounter += 1 Console.Write(element.ToString() & " ") If elementCounter = 2 Then Client1.BoundedQueue1.Dequeue() End If Next Console.WriteLine() Client1.PrintBoundedQueue1() Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output boundedQueue1 : 1 2 3
boundedQueue1 : 1 2 3
boundedQueue1 : 2 3 Hier krijgen we wel het gewenste resultaat.
Het creëren van kopies van de interne collectie, om de enumerator van deze kopie gebruik te laten maken, heeft natuurlijk een bepaalde performance overhead, zeker indien de collectie veel elementen bevat. Dit wordt dan ook vaak achterwege gelaten, waardoor men een niet robuuste iterator bekomt.
Een meer geraffineerde techniek ( dan het klonen van de interne collectie ) om een robuuste iterator te bekomen, zou eruit bestaan een registratiemechanisme op te bouwen, waarbij de enumerator verwittigd wordt bij het wijzigen van de collectie.
Een eenvoudige implementatie van dergelijk mechanisme wordt hieronder geïllustreerd : Visual Basic 2010 Broncode Namespace Example9 Class BoundedQueue : Implements IEnumerable Public Event Changed() Protected m_Items As Object() Protected m_Count As Integer Public Sub New( ByVal capacity As Integer) ReDim m_Items(capacity - 1) End Sub Public ReadOnly Property Count() As Integer Get Count = m_Count End Get End Property Public ReadOnly Property Capacity() As Integer Get Capacity = m_Items.Length End Get End Property Public Sub Enqueue( ByVal value As Object) If Count < Capacity Then m_Items(Count) = value RaiseEvent Changed() m_Count += 1 End If End Sub Public Function Peek() As Object Peek = m_Items(0) End Function Public Sub Dequeue() For index As Integer = 0 To Count - 2 m_Items(index) = m_Items(index + 1) Next m_Items(Count - 1) = 0 RaiseEvent Changed() m_Count -= 1 End Sub Public Overridable Function GetEnumerator() As IEnumerator _ Implements IEnumerable. GetEnumerator GetEnumerator = New BoundedQueueEnumerator(m_Items, Me) End Function Class BoundedQueueEnumerator : Implements IEnumerator Private m_ItemsChanged As Boolean Private Sub onItemsChanged() Handles m_BoundedQueue.Changed m_ItemsChanged = True End Sub Private WithEvents m_BoundedQueue As BoundedQueue Private m_Items As Object() Private m_Cursor As Integer = -1 Public Sub New( ByVal items As Object(), _ ByVal collection As BoundedQueue) m_Items = items m_BoundedQueue = collection End Sub Public Function MoveNext() As Boolean _ Implements IEnumerator. MoveNext If m_ItemsChanged Then Throw New InvalidOperationException( _ "Collection was modified; " & _ "enumeration operation may not execute.") Else m_Cursor += 1 MoveNext = (m_Cursor < m_BoundedQueue.Count) End If End Function Public ReadOnly Property Current() As Object _ Implements IEnumerator. Current Get If m_ItemsChanged Then Throw New InvalidOperationException( _ "Collection was modified; " & _ "enumeration operation may not execute.") Else Current = m_Items(m_Cursor) End If End Get End Property Public Sub Reset() Implements IEnumerator. Reset If m_ItemsChanged Then Throw New InvalidOperationException( _ "Collection was modified; " & _ "enumeration operation may not execute.") Else m_Cursor = -1 End If End Sub End Class End Class Class Client1 Private Shared m_BoundedQueuue1 As BoundedQueue Public Shared Sub Main() m_BoundedQueuue1 = New BoundedQueue(5) m_BoundedQueuue1.Enqueue(1) m_BoundedQueuue1.Enqueue(2) m_BoundedQueuue1.Enqueue(3) PrintBoundedQueue1() Console.Write( "boundedQueue1 : ") Dim elementCounter As Integer For Each element As Object In m_BoundedQueuue1 elementCounter += 1 Console.Write(element.ToString() & " ") If elementCounter = 2 Then m_BoundedQueuue1.Dequeue() End If Next End Sub Public Shared Sub PrintBoundedQueue1() Console.Write( "boundedQueue1 : ") For Each element As Object In m_BoundedQueuue1 Console.Write(element.ToString() & " ") Next Console.WriteLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Het registratiemechanisme is hier geïmplementeerd aan de hand van events, voor meer informatie betreffende events zie hiervoor het desbetreffende topic.
Er wordt naast de interne Object() ook een verwijzing naar de collectie zelf doorgegeven aan de enumerator. Bij het wijzigen van de collectie zal deze een Changed event afvuren, waarbij de enumerator verwittigd wordt van deze wijziging. Indien men vervolgens nog verder probeert te enumereren over deze enumerator zal deze enumerator een exception opwerpen (1).
Het optreden van een exceptie is in dergelijke situatie is aanvaardbaar, het is immers geen goede werkwijze om de collectie aan te passen terwijl men via een enumerator over de elementen enumereert.
Dit is ook wat er zou gebeuren bij het wijzigen van collecties van de meeste voorgedefinieerde collectie terwijl men aan het enumereren is over de enumerator : Visual Basic 2010 Broncode Namespace Example9 Class Client2 Public Shared Sub Main() Dim arrayList1 As New ArrayList arrayList1.Add(1) arrayList1.Add(2) arrayList1.Add(3) Dim elementCounter As Integer For Each element As Object In arrayList1 elementCounter += 1 Console.Write(element.ToString() & " ") If elementCounter = 2 Then arrayList1.Add(4) End If Next End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Ook hier treden dus dergelijke excepties op.
Dit artikel is gepubliceerd op zondag 31 juli 2011 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.
|