Реализация интерфейса 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.