Achtergrond van de vraag:
De For Each...Next-operator in Visual Basic stelt je in staat om de elementen van collecties of arrays te itereren. Vanaf VB6 was er ondersteuning voor iteratie over standaardcollecties, en in VB.NET over alle typen die een bepaald interface implementeren. Maar wanneer je aangepaste collecties wilt maken voor je eigen iteratie, is het belangrijk om de juiste infrastructuur correct te implementeren.
Probleem
Het is niet genoeg om alleen de opslag van elementen te implementeren; de For Each-operatoren eisen ondersteuning van het gespecialiseerde interface IEnumerable. Vaak waren de assemblages van aangepaste collecties beperkt tot de methoden Add/Get, en werd het object een "zwarte doos" voor iteratie. Dit belemmerde de integratie met de taalsstructuur van de iteratie en leidde tot fouten.
Oplossing
Om ervoor te zorgen dat For Each correct werkt met jouw collectie, moet je de interface IEnumerable implementeren, en voor ondersteuning van getypeerde elementen – IEnumerable(Of T). Je moet ook een eigen enumerator (Enumerator) maken die de interface IEnumerator implementeert (of IEnumerator(Of T) in generieke versies).
Voorbeeld:
Public Class IntCollection Implements IEnumerable(Of Integer) Private ReadOnly items As New List(Of Integer)() Public Sub Add(value As Integer) items.Add(value) End Sub Public Function GetEnumerator() As IEnumerator(Of Integer) _ Implements IEnumerable(Of Integer).GetEnumerator Return items.GetEnumerator() End Function Private Function IEnumerable_GetEnumerator() As IEnumerator _ Implements IEnumerable.GetEnumerator Return GetEnumerator() End Function End Class ' Gebruik: Dim col As New IntCollection() col.Add(10) col.Add(20) col.Add(30) For Each n As Integer In col Console.WriteLine(n) Next
Kernpunten:
Is het mogelijk om For Each te implementeren zonder de interface IEnumerable, gewoon door een method GetEnumerator te schrijven?
Nee, Visual Basic vereist officiële ondersteuning van de interface IEnumerable/IEnumerable(Of T) zodat de compiler de collectie als compatibel met For Each herkent.
Moet de enumerator een aparte klasse zijn?
Nee, als de gegevensbasis van elementen een standaard enumerator ondersteunt (bijvoorbeeld als je List(Of T) aggregeert), kan je de GetEnumerator teruggeven. Alleen in complexe scenario's kan je een eigen klasse-implementatie van IEnumerator nodig hebben.
Is het mogelijk om de collectie binnen de For Each-lus te wijzigen?
Nee, het wijzigen van de collectie tijdens de iteratie leidt tot een InvalidOperationException. Voor correcte verwerking zijn speciale strategieën nodig (bijvoorbeeld het kopiëren van de lijst voorafgaand aan de iteratie of het gebruik van indexering).
In het bedrijf was een eigen klasse MyCollection ontwikkeld met toevoeging en verwijdering via een array, maar zonder implementatie van de interface IEnumerable. For Each werkte niet, dus moest je gewone lussen en een openbaar veld met elementen gebruiken.
Voordelen:
Nadelen:
De klasse werd opnieuw ingericht door IEnumerable(Of T) te implementeren. Nadat dit was gedaan, kon de collectie eenvoudig en standaard in For Each worden gebruikt en werd het ook compatibel met LINQ.
Voordelen:
Nadelen: