Historia pytania
Iteratory stały się ważną częścią języków programowania z pojawieniem się kolekcji, które trzeba przeszukiwać. W Visual Basic od wersji .NET pojawiło się wsparcie dla IEnumerable/IEnumerator, umożliwiające implementację własnych iterowanych kolekcji i wykorzystanie pętli For Each do wygodnego i zwięzłego dostępu do zawartości kolekcji.
Problem
Wielu programistów ogranicza się do korzystania tylko z wbudowanych kolekcji lub niewłaściwie zaimplementowuje interfejs IEnumerable, przez co niemożliwe jest przeszukiwanie obiektów za pomocą For Each. Ponadto, problemy sprawia implementacja i utrzymanie stanu iteratora przez Current i MoveNext.
Rozwiązanie
Aby wspierać iteracje, typ implementuje interfejs IEnumerable (lub generyczny IEnumerable(Of T)). Zaimplementuj funkcję GetEnumerator, która zwraca obiekt implementujący IEnumerator. W VB.NET można też stosować metody iteratorowe z kluczowym słowem Yield (od VB 2015), co znacznie upraszcza realizację przeszukiwania.
Przykład kodu:
Imports System.Collections Public Class SimpleCollection Implements IEnumerable Private arr() As Integer = {1, 2, 3} Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator For Each i In arr Yield i ' dla VB 2015+ Next End Function End Class ' Użycie: For Each n As Integer In New SimpleCollection() Console.WriteLine(n) Next
Kluczowe cechy:
Czy konieczne jest zaimplementowanie obu interfejsów: IEnumerable i IEnumerator, aby wspierać For Each?
Nie, wystarczy implementacja IEnumerable i zwrócenie zgodnego iteratora. Jeśli chcesz zaimplementować przeszukiwanie, możesz użyć już istniejącego IEnumerator z zagnieżdżonej kolekcji. Ale dla pełnej personalizacji konieczna jest implementacja obu części.
Przykład kodu:
Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator Return arr.GetEnumerator() ' używamy wbudowanego iteratora tablicy End Function
Czy można zaimplementować IEnumerable tylko jawnie (Explicit Interface Implementation) w Visual Basic?
Tak, można zaimplementować interfejs jawnie, szczególnie gdy Twoja kolekcja już ma własny GetEnumerator i chcesz ukryć implementację interfejsu. Nie uda się wywołać takiego GetEnumerator bezpośrednio, ale For Each będzie działać.
Przykład kodu:
Public Function IEnumerable_GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator ' ...ciało metody End Function
Co się stanie, jeśli GetEnumerator zwróci Nothing?
Zostanie zgłoszone wyjątek w momencie pierwszej próby przeszukiwania kolekcji, ponieważ For Each oczekuje poprawnego iteratora.
Klasa kolekcji nie zaimplementowała IEnumerable, a osobna metoda Next została utworzona do przeszukiwania elementów. W efekcie nie można jej używać w uniwersalnych metodach lub For Each, trzeba pisać dodatkowe metody.
Zalety:
Wady:
Programista zaimplementował IEnumerable i GetEnumerator, kolekcja teraz działa z For Each i integruje się z LINQ.
Zalety:
Wady: