Historia pytania:
Operator For Each...Next w Visual Basic umożliwia iterację przez elementy kolekcji lub tablic. Od VB6 wspierano iterację po standardowych kolekcjach, a w VB.NET — po wszystkich typach implementujących określony interfejs. Jednak gdy zachodzi potrzeba tworzenia niestandardowych kolekcji do własnej iteracji, ważne jest poprawne wdrożenie odpowiedniej infrastruktury.
Problem
Nie wystarczy wdrożyć przechowywania elementów — operatory For Each wymagają wsparcia specjalizowanego interfejsu IEnumerable. Często budowy niestandardowych kolekcji ograniczały się do metody Add/Get, obiekt przemieniał się w "czarną skrzynkę" do iteracji. To utrudniało integrację z językową strukturą iteracji i prowadziło do błędów.
Rozwiązanie
Aby For Each poprawnie działał z twoją kolekcją, należy wdrożyć interfejs IEnumerable, a dla wsparcia pracy z typowanymi elementami — IEnumerable(Of T). Należy także stworzyć własny enumerator (Enumerator), implementujący interfejs IEnumerator (lub IEnumerator(Of T) w wersjach ogólnych).
Przykład:
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 ' Użycie: 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
Kluczowe cechy:
Czy można zaimplementować For Each bez interfejsu IEnumerable, po prostu pisząc metodę GetEnumerator?
Nie, Visual Basic wymaga oficjalnego wsparcia interfejsu IEnumerable/IEnumerable(Of T), aby kompilator rozpoznał kolekcję jako kompatybilną z For Each.
Czy enumerator musi być oddzielną klasą?
Nie, jeśli baza danych elementów wspiera standardowy enumerator (na przykład, jeśli agregujesz List(Of T)), można zwracać jego GetEnumerator. Tylko w trudnych scenariuszach może być potrzebna własna klasa implementująca IEnumerator.
Czy można modyfikować kolekcję wewnątrz pętli For Each?
Nie, zmiana kolekcji podczas iteracji spowoduje InvalidOperationException. Dla poprawnego przetwarzania potrzebne są specjalne strategie (na przykład kopiowanie listy przed iteracją lub użycie indeksowania).
W firmie zaprojektowano własną klasę MyCollection z dodawaniem i usuwaniem przez tablicę, ale bez implementacji interfejsu IEnumerable. For Each nie działał, trzeba było używać zwykłych pętli i publicznego pola z elementami.
Zalety:
Wady:
Klasę przerobiono, implementując IEnumerable(Of T). Po tym kolekcja łatwo i standardowo była wykorzystywana w For Each, a także stała się kompatybilna z LINQ.
Zalety:
Wady: