Henry手记—.NET数据结构对象补遗之单链表(三)
韩睿 ( 06/15/2003)
3.8 Item属性
Item属性提供给VB.NET按list(2)这样的方式操作列表,也就能赋予单链表和ArrayList数组一样的索引方式,尽管在实现上效率不如数组,但这样方便的操作手段我们不能放过:
Default Public Overridable Property Item( _
ByVal index As Integer) As Object Implements IList.Item
'Item属性,读写链表的索引处结点的数据值
Get
Validate(index)
Return FindByIndex(index).Data
End Get
Set(ByVal Value As Object)
Validate(index, Value)
FindByIndex(index).Data = Value
End Set
End Property
同样的,我们还提供计数功能,利用的就是上述各种操作中都在进行更改的nodeCount成员。
Public Overridable ReadOnly Property Count() As Integer _
Implements IList.Count
'返回链表元素总数
Get
Return nodeCount
End Get
End Property
到这里,我们是不是已经实现了基本的链表操作功能了?但是,还有一些重要机制并没有揭示给读者朋友,让我们继续吧。
3.9 For Each循环枚举的实现
For Each是个很强大的遍历方法,要实现这个功能,链表类必须实现IEnumerable接口,在IList我们通过实现继承自IEnumerable的GetEnumerator函数来达到这一目标。但该函数返回的是IEnumerator枚举器类型,因此我们要实现自己的枚举器来达到对链表的处理:
IEnumerator 是所有枚举数的基接口。枚举数只允许读取集合中的数据。枚举数无法用于修改基础集合。IEnumerator接口支持两种方法和一个属性。MoveNext方法能在集合中一次移动一条记录。Reset方法能使枚举器复位到集合的起始。Current只读属性能从集合返回当前记录。
最初,枚举数被定位于集合中第一个元素的前面。Reset 也将枚举数返回到此位置。在此位置,调用 Current 会引发异常。因此,在读取 Current 的值之前,必须调用 MoveNext 将枚举数提前到集合的第一个元素。
在调用 MoveNext 或 Reset 之前,Current 返回同一对象。MoveNext 将 Current 设置为下一个元素。
在传递到集合的末尾之后,枚举数放在集合中最后一个元素后面,且调用 MoveNext 会返回 false。如果最后一次调用 MoveNext 返回 false,则调用 Current 会引发异常。若要再次将 Current 设置为集合的第一个元素,可以调用 Reset,然后再调用 MoveNext。
只要集合保持不变,枚举数就保持有效。如果对集合进行了更改(例如添加、修改或删除元素),则该枚举数将失效且不可恢复,并且下一次对 MoveNext 或 Reset 的调用将引发 InvalidOperationException。如果在 MoveNext 和 Current 之间修改集合,那么即使枚举数已经无效,Current 也将返回它所设置成的元素。现在您就会理解我们在前面不断看到的那个version的用法了。
由于我们在之前把ListNode定义为嵌套类,同时需要调用SLList类的成员,所以把自定义的实现IEnumerator接口的类置入SLList类中,成为第二个嵌套类:
Public Overridable Function GetEnumerator() As IEnumerator _
Implements IList.GetEnumerator
Return New SLListEnumerator(Me)
End Function
下面就是实现IEnumerator接口的类了。
Protected Class SLListEnumerator
Implements IEnumerator
'嵌套在SLList内部的计数器类
Protected list As SLList
Protected currentElement As Object
Protected currentNode As ListNode '嵌套在内部才可以使用这个类
Protected version As Integer '内部版本记录,用于比对链表是否已有改变
Public Sub New(ByVal list As SLList)
'初始化
Me.list = list
Me.version = list.version
Me.currentElement = list
Me.currentNode = list.head
End Sub
Protected Overridable Sub VerifyListIsUnchanged()
'判断版本是否在枚举器创建后还发生了改变
If Not version = list.version Then
Throw New InvalidOperationException( _
"该链表在枚举器创建后发生了改变")
End If
End Sub
Public Overridable ReadOnly Property Current() As Object _
Implements IEnumerator.Current
'返回当前记录
Get
'判断是否已经到了链表尾或是否调用了MoveNext查找记录
If currentElement Is list Then
If currentNode Is list.head Then
Throw New InvalidOperationException( _
"Current方法在MoveNext被调用前是无效的.")
Else
Throw New InvalidOperationException( _
"已到达集合尾,因此Current方法无效")
End If
End If
Return currentElement
End Get
End Property
Public Overridable Function MoveNext() As Boolean _
Implements IEnumerator.MoveNext
'将枚举器继续移到下一个对象
VerifyListIsUnchanged()
If Not currentNode.NextNode Is Nothing Then
currentNode = currentNode.NextNode
currentElement = currentNode.Data
Return True
Else
currentElement = list
currentNode = list.head
Return False
End If
End Function
Public Overridable Sub Reset() Implements IEnumerator.Reset
'将枚举器重置为其初始位置
VerifyListIsUnchanged()
currentNode = list.head
currentElement = list
End Sub
End Class
----
声明:本文版权与解释权归韩睿所有,如需转载,请保留完整的内容及此声明。
QQ: 18349592
E-Mail: henry7685@hotmail.com
请访问本人专栏:http://www.csdn.net/develop/author/netauthor/Latitude/