Реализация интерфейса IEnumerable предполагает стандартную реализацию перечислителя. Однако мы можем не полагаться на стандартную
реализацию, а создать свою логику итератора с помощью ключевых слов Iterator и Yield.
Конструкция итератора представляет метод, в котором используется ключевое слово Yield для перебора по коллекции или массиву. Например,
перепишем определенный в прошлой теме метод GetEnumerator
в классе Library, применив итераторы:
Public Iterator Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator For i As Integer = 0 To books.Length - 1 Yield books(i) Next End Function
Метод GetEnumerator()
теперь является итератором. И при переборе всех элементов в объекте Library через цикл For Each
будет идти к обращение к вызову Yield books(i)
. При обращении к оператору Yield
будет сохраняться текущее
местоположение. И когда метод For Each перейдет к следующей итерации для получения нового объекта, итератор начнет выполнения с этого местоположения.
И в основной программе в цикле For Each стандартным образом:
For Each b As Book In library Console.WriteLine(b.Name) Next
Хотя выше в методе GetEnumerator()
применялся перебор массива в цикле for, но это необязательно делать.
Можно просто определить несколько вызовов оператора Yield
:
Public Iterator Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator Yield books(0) Yield books(1) Yield books(2) End Function
Здесь также при вызове оператора Yield
итератор запоминает текущее местоположение и при последующих вызовах начинает с него.
Хотя выше для создания итератора применялся метод GetEnumerator
, но это необязательно, так как оператор Yield
можно использовать внутри любого метода. Единственное ограничение - такой метод должен возвращать объект интерфейса IEnumerable
.
Подобные методы еще называют именованными итераторами.
Создадим такой именованный итератор в классе Library:
Class Book Public Property Name() As String Sub New(name As String) Me.Name = name End Sub End Class Class Library Dim books As Book() Sub New() books = New Book() {New Book("Отцы и дети"), New Book("Война и мир"), New Book("Евгений Онегин")} End Sub Public ReadOnly Property Length As Integer Get Return books.Length End Get End Property Default Public Property Item(index As Integer) As Book Get Return books(index) End Get Set(value As Book) books(index) = value End Set End Property Public Iterator Function GetBooks(max As Integer) As IEnumerable For i As Integer = 0 To max If i = books.Length Then Exit Function Else Yield books(i) End If Next End Function End Class
Здесь в качестве итератора выступает функция Public Iterator Function GetBooks(max As Integer) As IEnumerable
, которая принимает в качестве параметра
количество выводимых объектов. В процессе работы программы может сложиться, что его значение будет больше, чем длина массива books. И чтобы не
произошло ошибки, вызывается завершение функции с помощью операторов Exit Function.
Теперь применим итератор в программе:
Dim library As New Library() For Each b As Book In library.GetBooks(5) Console.WriteLine(b.Name) Next
Вызов library.GetBooks(5)
возвращает набор из не более чем 5 объектов Book. Но поскольку по умолчанию объект library содержит
всего три таких объекта, то в методе GetBooks
после трех операций сработает завершение функции Exit Function
.