Introduction
This article demonstrates how to implement both
IEnumerable(Of T)
and
IEnumerator(Of T)
in VB.NET and use them to read data in a collection.
Background
How can I do iteration over a collection? ....... for VB coders,
almost the question has no answer, and if you search over the internet
you might be lucky to find enough information which may answer such a
question. C# coders are really lucky with
Yield
statement and VB.NET coders must suffer a little .....
What I Shall Implement?
Actually, there is a time when you may need to build your own
iterator, such a case may occur when you need to read data from a
certain collection, and to build your own Iterator in VB.NET you must
implement both
IEnumerator(Of T)
and
IEnumerable(Of T)
either in one single class or in two separate classes. Also you must implement the methods and properties related to both
IEnumerable(Of T)
&
IEnumerator(Of T)
interfaces. The methods and properties which shall be implemented for each interface are listed below.
IEnumerable(Of T) Methods
Since
IEnumerable(Of T)
is inherited from
IEnumerable
interface, you will have to implement two similar
Function
s that are almost identical in names, but they are differ in the return type.
Name |
Description |
Interface |
Return Type |
GetEnumerator |
Function |
IEnumerable(Of T) |
Generic |
GetEnumerator1 |
Function |
IEnumerable |
Non Generic |
Which function shall be implemented? Always you shall implement the
strongly typed generic function and be sure to change the non-generic
function to
Private
and call the generic method inside it, if required to access the
private
method by any means, you may just cast through the interface.
Public FunctionGetEnumerator() As System.Collections.Generic.IEnumerator(
Of T) ImplementsSystem.Collections.Generic.IEnumerable(Of T).GetEnumerator
' Add implementation here
End Function
Private FunctionGetEnumerator1() _
As System.Collections.IEnumerator ImplementsSystem.Collections.IEnumerable.GetEnumerator
' Just call the generic Function
ReturnGetEnumerator()
End Function
IEnumerator(Of T) Methods & Properties
Also, since
IEnumerator(of T)
is inherited from both
IDisposable
&
IEnumerator
interfaces,
then you will have to implement two identical read only properties that
almost have the same names, but differ in the return type, the one with
generic return type comes from
IEnumerable(Of T)
and the other one with non-generic return type comes from
IEnumerable
plus two methods with non-generic return type comes from
IEnumerable
interface. In addition to that, you will have to implement the methods related to
IDisposable
interface.
Name |
Description |
Interface |
Return Type |
Current |
ReadOnly Property |
IEnumerable(Of T) |
Generic |
Current1 |
ReadOnly Property |
IEnumerable |
Non Generic |
MoveNext |
Method |
IEnumerable |
Non Generic |
Reset |
Method |
IEnumerable |
Non Generic |
Which property shall be implemented? As we did above with
GetEnumerator
, you shall implement the strongly typed generic property and be sure to change the non-generic property to
Private
and return the generic property inside it.
Public ReadOnly Property Current As T _
Implements System.Collections.Generic.IEnumerator(Of T).Current
Get
' Add implementation here
End Get
End Property
Private ReadOnly Property Current1 As Object _
ImplementsSystem.Collections.IEnumerator.Current
Get
' Just return the generic property value
Return Me.Current
End Get
End Property
Public FunctionMoveNext() As BooleanImplements System.Collections.IEnumerator.MoveNext
' Add implementation here
End Function
Public Sub Reset() Implements System.Collections.IEnumerator.Reset
' Add implementation here
End Sub
' IDisposable Support goes here
IDisposable Methods & Properties
Name |
Description |
Interface |
Return Type |
Dispose |
Method |
IDisposable |
Nothing |
Dispose |
Method |
IDisposable |
Boolean |
What to do with
IDisposable
methods? Actually nothing to be implemented here simply deletes or comments the code except the
Dispose
method. Just be sure to change it to
Private
.
#Region "IDisposable Support"
'Private disposedValue As Boolean ' To detect redundant calls
'' IDisposable
'Protected Overridable Sub Dispose(ByVal disposing As Boolean)
'If Not Me.disposedValue Then
'If disposing Then
'' TODO: dispose managed state (managed objects).
'End If
'' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
'' TODO: set large fields to null.
'End If
'Me.disposedValue = True
'End Sub
' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean)
' above has code to free unmanaged resources.
'Protected Overrides Sub Finalize()
'' Do not change this code.Put cleanup code in Dispose(ByVal disposing As Boolean) above.
'Dispose(False)
'MyBase.Finalize()
'End Sub
' This code added by Visual Basic to correctly implement the disposable pattern.
Private Sub Dispose()Implements IDisposable.Dispose
'' Do not change this code.Put cleanup code in Dispose(ByVal disposing As Boolean) above.
'Dispose(True)
'GC.SuppressFinalize(Me)
End Sub
#End Region
Remember: Always, you shall implement the generic methods & properties, in addition to that the nongeneric MoveNext and
Reset methods.
Build Iterator
To demonstrate how to build iterator, consider you have an interface and/or a Component which implements that interface.
Imports System.Windows.Forms
Imports System.Drawing
Public Interface IRiverNile
Property Font() As Font
Property Text() As String
Property Description() AsString
Property Image() As Image
Property Disabled() AsBoolean
Property control As Control
End Interface
Public Class RiverNileComponent
Implements IRiverNile
' rest of code goes here
End Class
- Consider that you want to write your own Collection for that interface or component, and your collection is inherited from
Collectionsbase
Class and also implements IList(Of T)
.
Public Class RiverNileCollection
Inherits Collectionsbase
Implements IList(Of IRiverNile)
' rest of the codes goes here
End Class
- Add your nested
Iterator
Class, at the end of the Collection
Class.
- Define your
private
values in the Iterator
Class, and add a constructor for the iterator.
Public Class RiverNeCollection
Inherits Collectionbase
Implements IList(Of IRiverNile)
' rest of the codes goes here
Private Class RiverNileCollectionEnumerator
Implements IEnumerator(Of IRiverNile)
Implements IEnumerable(Of IRiverNile)
Private _collection As RiverNileCollection
Private _index As Integer
Private _current As IRiverNile
Public Sub New(ByVal collection As RiverNileCollection)
MyBase.New()
_collection = collection
_index = -1
_current = CType(Nothing, IRiverNile)
End Sub
End Class
End Class
- As explained before, the nongeneric function shall be changed to
private
and shall return the generic function value.
- The generic function shall return the New
Iterator
value.
' rest of the codes goes here
Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(
Of IRiverNile) Implements System.Collections.Generic.IEnumerable(
Of IRiverNile).GetEnumerator
Return New RiverNileCollectionEnumerator(_collection)
End Function
Private Function GetEnumerator1() As System.Collections.IEnumerator _
Implements System.Collections.IEnumerable.GetEnumerator
Return GetEnumerator()
End Function
' rest of the codes goes here
- As explained earlier, the nongeneric
Current
Property shall be Private
and shall return the value of generic Current
Property.
- The generic
Current
Property then return the correspondent private
, an InvalidOperationExpected
shall be thrown if your value is nothing.
' rest of the codes goes here
Public ReadOnly Property Current As IRiverNile Implements _
System.Collections.Generic.IEnumerator(Of IRiverNile).Current
Get
If _current Is Nothing Then
Throw New InvalidOperationException()
End If
Return _current
End Get
End Property
Private ReadOnly Property Current1 As Object Implements _
System.Collections.IEnumerator.Current
Get
Return Me.Current
End Get
End Property
' rest of the codes goes here
- Implement the nongeneric
MoveNext
and Reset
Methods.
- The
MoveNext
Method shall return False
to avoid going beyond the end of the collection when the index is equal to the collection Count
Property, else will return True
and then you set the current value
' rest of the codes goes here
Public Function MoveNext() As Boolean Implements System.Collections.IEnumerator.MoveNext
If _index = _collection.Count Then
' Avoids going beyond the end of the collection.
Return False
Else
'Set current box to next item in collection.
_current = _collection(_index)
End If
Return True
End Function
Public Sub Reset() Implements System.Collections.IEnumerator.Reset
_index = -1
_current = CType(Nothing, IRiverNile)
End Sub
- Once you complete your iterator, return a New Value of it in the
Collection
Class GetEnumerator
Method.
' Rest of code goes here
Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(
Of IRiverNile) Implements System.Collections.Generic.IEnumerable(
Of IRiverNile).GetEnumerator
Return New RiverNileCollectionEnumerator(Me)
End Function
' Rest of code goes here
Generics Iterator
The problem with the above iterator is that you may have to write an
iterator
class for any
collection
class, so instead of that, it is better to write a generic class that can be used with any
List(Of T)
or any
Collection(of T)
and here it is.
'''
''' Copyright © Omar Amin Ibrahim 2011
''' silverlight1212@yahoo.com
'''
'''
'''
Public Class RiverNileListEnumerator(Of T)
Implements IEnumerable(Of T)
Implements IEnumerator(Of T)
#Region " Fields "
Private _list As IList(Of T)
Private _index As Integer
Private _current As T
#End Region
#Region " Constructor "
Public Sub New(ByVal list As IList(Of T))
MyBase.New()
_list = list
_index = -1
_current = CType(Nothing, T)
End Sub
#End Region
#Region " Properties "
Public ReadOnly Property Current As T _
Implements System.Collections.Generic.IEnumerator(Of T).Current
Get
If _current Is Nothing Then
Throw New InvalidOperationException()
End If
Return _current
End Get
End Property
Private ReadOnly Property System_Collections_Current As _
Object Implements System.Collections.IEnumerator.Current
Get
Return Me.Current
End Get
End Property
#End Region
#Region " Methods "
Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of T) _
Implements System.Collections.Generic.IEnumerable(Of T).GetEnumerator
Return New RiverNileListEnumerator(Of T)(Me._list)
End Function
Private Function System_Collections_GetEnumerator() _
As System.Collections.IEnumerator_
Implements System.Collections.IEnumerable.GetEnumerator
Return Me.GetEnumerator
End Function
Public Function MoveNext() As Boolean Implements _
System.Collections.IEnumerator.MoveNext
_index = _index + 1
If _index = _list.Count Then
Return False
Else
_current = _list(_index)
End If
Return True
End Function
Public Sub Reset() Implements System.Collections.IEnumerator.Reset
_index = -1
_current = CType(Nothing, T)
End Sub
Private Sub System_IDisposable_Dispose() Implements IDisposable.Dispose
' do nothing
End Sub
#End Region
End Class ' RiverNileListEnumerator
Implementation with Collection
'''
''' Copyright © Omar Amin Ibrahim 2011
''' silverlight1212@yahoo.com
'''
'''
'''
Public Class RiverNileCollectionEnumerator(Of T)
Implements IEnumerable(Of T)
Implements IEnumerator(Of T)
#Region " Fields "
Private _collectiont As ICollection(Of T)
Private _index As Integer
Private _current As T
#End Region
#Region " Constructor "
Public Sub New(ByVal collection As ICollection(Of T))
MyBase.New()
_collectiont = collection
_index = -1
_current = CType(Nothing, T)
End Sub
#End Region
#Region " Properties "
Public ReadOnly Property Current As T
Get
If _current Is Nothing Then
Throw New InvalidOperationException()
End If
Return _current
End Get
End Property
Private ReadOnly Property System_Collections_Generic_Current _
As T Implements System.Collections.Generic.IEnumerator(Of T).Current
Get
Return Me.Current
End Get
End Property
Private ReadOnly Property System_Collections_Current _
As Object Implements System.Collections.IEnumerator.Current
Get
Return Me.Current
End Get
End Property
#End Region
#Region " Methods "
Public Function GetEnumerator() As IEnumerator(Of T)
Return New RiverNileCollectionEnumerator(Of T)(Me._collectiont)
End Function
Public Function MoveNext() As Boolean
_index = _index + 1
If _index = _collectiont.Count Then
Return False
Else
_current = _collectiont(_index)
End If
Return True
End Function
Public Sub Reset()
_index = -1
_current = CType(Nothing, T)
End Sub
Private Function System_Collections_Generic_GetEnumerator() _
As System.Collections.Generic.IEnumerator(Of T) Implements _
System.Collections.Generic.IEnumerable(Of T).GetEnumerator
Return Me.GetEnumerator
End Function
Private Function System_Collections_GetEnumerator() As _
System.Collections.IEnumerator Implements _
System.Collections.IEnumerable.GetEnumerator
Return Me.GetEnumerator
End Function
Private Function System_Collections_MoveNext() As Boolean _
Implements System.Collections.IEnumerator.MoveNext
Return MoveNext()
End Function
Private Sub System_Collections_Reset() _
Implements System.Collections.IEnumerator.Reset
Me.Reset()
End Sub
Private Sub System_IDisposable_Dispose() Implements IDisposable.Dispose
' do nothing
End Sub
#End Region
End Class ' RiverNileCollectionEnumerator
Iterator
'''
''' Copyright © Omar Amin Ibrahim 2011
''' silverlight1212@yahoo.com
'''
'''
Public NotInheritable Class RiverNileGenerics
Public Shared Function Iterator(Of T)(ByVal list As IList(Of T)) _
As RiverNileListEnumerator(Of T)
If (list Is Nothing) Then
Throw New ArgumentNullException("list")
End If
Return New RiverNileListEnumerator(Of T)(list)
End Function
Public Shared Function Iterator(Of T)(ByVal collection As ICollection(Of T)) _
As RiverNileCollectionEnumerator(Of T)
If (collection Is Nothing) Then
Throw New ArgumentNullException("collection")
End If
Return New RiverNileCollectionEnumerator(Of T)(collection)
End Function
End Class ' RiverNileGenerics
Using the Code
It is simple. Try it.
Imports RiverNile.RiverNileGenerics
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Dim ColorCollection As ICollection(Of KnownColor) = _
New List(Of KnownColor) From {KnownColor.Aqua, KnownColor.Red, KnownColor.Black}
For Each clr As KnownColor In Iterator(ColorCollection)
ListBox1.Items.Add(clr)
Next
Dim StringList As IList(Of String) = New List(Of String) _
From {"Omar", "Amin", "Ibrahim"}
For Each Str As String In Iterator(StringList)
ListBox2.Items.Add(Str)
Next
End Sub
End Class
How To Use It With a Collection
- Define your collection.
- Implements
IList(Of n)
.
- Just call the shared method from the
RiverNileGenerics
Class as below:
Imports RiverNile.RiverNileGenerics
Public Class BarCollection
Inherits CollectionBase
Implements IList(Of Bar)
#Region " Fields "
Private owner As BarControl
#End Region
#Region " Constructor "
Public Sub New(ByVal containerControl As BarControl)
Me.owner = containerControl
End Sub
#End Region
#Region " Methods "
Public Overloads Function GetEnumerator() As IEnumerator(Of Bar)
Return Iterator(Of Bar)(Me)
End Function
Private Function System_Collections_Generic_GetEnumerator() _
As System.Collections.Generic.IEnumerator(Of Bar) _
Implements System.Collections.Generic.IEnumerable(Of Bar).GetEnumerator
Return Me.GetEnumerator
End Function
' rest of the codes goes here
#End Region
End Class
References
Download
Comments
Post a Comment