Implementing a List Iterator
Although I’m going to show you a specific iterator class, you should be thinking of iterators as a general concept. Any collection of data might need an iterator class, regardless of its organization. In fact, the interface of all iterator classes ought to look the same, no matter what objects they iterate.
CListWalker has the following internal variables to keep track of its state:
‘ Connect back to parent collection
Private connect As CList
‘ Current link
Private lnkCur As CLink
This is typical. Any iterator has these two requirements: a reference (connect) to the collection it will iterate through and at least one state variable to keep track of its current position (lnkCur).
First let’s look at an example of how an iterator connects to its collection. CListWalker does this with the Attach method:
‘ Attach a list to iterator
Sub Attach(connectA As CList)
‘ Initialize position in collection
Set connect = connectA
End Sub
If you look at the real code in LISTWALK.CLS, you’ll see that it is actually a little more complicated, but the other parts don’t matter at this point.
Clients must call Attach before doing anything else with the class. In fact, it would be part of the constructor in languages that support object initialization at creation. The connection between CList and CListWalker is two-way. CList provides a Head friend property for CListWalker, and CListWalker provides a CurLink friend property for CList:
‘ Expose current link to friends
Friend Property Get CurLink() As CLink
Set CurLink = lnkCur
End Property
Friend Property Set CurLink(lnkCurA As CLink)
Set lnkCur = lnkCurA
End Property
CListWalker also provides access to the current data through its Item property:
‘ Default member
Property Get Item() As Variant
If IsObject(lnkCur.Item) Then
Set Item = lnkCur.Item
Else
Item = lnkCur.Item
End If
End Property
Here’s another default Item. The lnkCur member is of type CLink, and CLink also has a default Item member. So, in theory, you could omit Item from lnkCur. But what would the following statement mean?
Set Item = lnkCur
Would you be setting the Item property to the link or to the default member of the link? Do you want to find out, or do you want your customers to find out? This is why Joe Hacker claims that default properties are an invention of the devil. Personally, I kind of like them, but I don’t deny that they can be confusing and dangerous.
You might as well get used to this little block of code. Any container class based on variants needs to check for objects before any assignment is made. Someday you’re going to forget to do this. Let’s hope you find the bug easier to identify and fix than I did.
The real work of the iterator is done by the More method. It figures out whether there is more data in the list and, if so, advances to the next member. I wanted to name the More method Next, because this is a common name for the equivalent method in other languages. Visual Basic already uses Next as a keyword, so I had to choose another name:
‘ Report whether there are more links to iterate
Function More() As Boolean
If lnkCur Is Nothing Then
‘ Don’t skip the first time through
Set lnkCur = connect.Head
Else
‘ Skip to the next item
Set lnkCur = lnkCur.NextLink
End If
‘ When the next link is nothing, we’re done (handles empty list)
If Not lnkCur Is Nothing Then More = True
End Function
Often, a More method has to take a different action the first time through the list than on subsequent passes. You’ll see more Mores with different organizations before the end of this chapter.