-
Oct 11th, 2011, 04:18 PM
#1
[.NET 3.5+] RecursiveIterator - Iterate Hierarchical Structures With One Class
This is a class that allows you to iterate over collections that would normally require recursion. It's quite simple, and actually, is really handy.
Basically, you instantiate the class and then you can perform a For Each on the class which will enumerate through every element in the hierarchical structure.
Here are a couple very common uses:
- Enumerating Controls
This is something you can run on your Form to get every control. Another way you could use it is if you only wanted every control in a Panel. You would just specify the Panel's Controls property in place of the Form's Controls. Since a Form derives from Control, you can simply specify the Form's instance as the first argument to the constructor. (As opposed to the TreeView example below.)
Code:
Dim iterator = New RecursiveIterator(Of Control, Control.ControlCollection) _
(Me, Function(c) c.Controls)
For Each c In iterator
Console.WriteLine("Name: {0}", c.Name)
Next
- Enumerating TreeViews
This is a somewhat special case because a TreeView is not of type TreeNode. For this case, you would specify the starting collection instead of the actual TreeView.
Code:
Dim iterator = New RecursiveIterator(Of TreeNode, TreeNodeCollection) _
(Me.TreeView1.Nodes, Function(n) n.Nodes)
For Each n In iterator
Console.WriteLine("Name: {0}, Level: {1}", n.Name, n.Level)
Next
- Your custom classes
You should find that this class works quite easily with your custom classes. Here is an example structure that this class could enumerate.
Code:
Public Class Person
'//properties
Private _children As PersonCollection
Public ReadOnly Property Children() As PersonCollection
Get
Return Me._children
End Get
End Property
End Class
Public Class PersonCollection
Inherits List(Of Person)
End Class
Code:
Dim mom = New Person()
Dim iterator = New RecursiveIterator(Of Person, PersonCollection) _
(mom, Function(p) p.Children)
For Each p In iterator
'//... all of mom's children, grand-children, grand-grand children etc...
Next
The trick is telling the class how to retrieve the collection of the element's children. This is done with a Func(Of T1, T2) delegate.
Here is the simple class:
Code:
''' <summary>
''' Represents a class that can enumerate through hierarchical based structures.
''' </summary>
''' <typeparam name="TElement">The type of the element.</typeparam>
''' <typeparam name="TSource">The type of the collection of elements.</typeparam>
Public Class RecursiveIterator(Of TElement, TSource As IEnumerable)
Implements IEnumerable(Of TElement)
'//fields
Private elements As List(Of TElement)
Private startingSource As TSource
Private selector As Func(Of TElement, TSource)
'//properties
''' <summary>
''' Gets the number of elements contained in the iterator.
''' </summary>
Public ReadOnly Property Count() As Integer
Get
Return Me.elements.Count
End Get
End Property
''' <summary>
''' Gets the element at the specified index.
''' </summary>
''' <param name="index">The index of the item to retrieve.</param>
Default Public ReadOnly Property Item(ByVal index As Integer) As TElement
Get
Return Me.elements(index)
End Get
End Property
'//constructors
''' <summary>
''' Initializes a new instance of the
''' <see cref="RecursiveIterator(Of TElement, TSource)">
''' RecursiveIterator(Of TElement, TSource)
''' </see>
''' class.
''' </summary>
''' <param name="startingSource">
''' The initial source from which the iterator will fill.
''' </param>
''' <param name="sourceSelector">
''' The selector that retrieves the enumerating
''' source from each element.
''' </param>
Public Sub New(ByVal startingSource As TSource, _
ByVal sourceSelector As Func(Of TElement, TSource))
Me.elements = New List(Of TElement)()
Me.startingSource = startingSource
Me.selector = sourceSelector
'//fill the iterator
Me.RecursivePopulate(Me.startingSource)
End Sub
''' <summary>
''' Initializes a new instance of the
''' <see cref="RecursiveIterator(Of TElement, TSource)">
''' RecursiveIterator(Of TElement, TSource)
''' </see>
''' class.
''' </summary>
''' <param name="startingElement">
''' The initial element from which the iterator will fill.
''' </param>
''' <param name="sourceSelector">
''' The selector that retrieves the enumerating
''' source from each element.
''' </param>
Public Sub New(ByVal startingElement As TElement, _
ByVal sourceSelector As Func(Of TElement, TSource))
Me.New(sourceSelector(startingElement), sourceSelector)
End Sub
'//methods
Private Sub RecursivePopulate(ByVal collection As TSource)
For Each element As TElement In collection
Me.elements.Add(element)
Me.RecursivePopulate(Me.selector(element))
Next
End Sub
Private Function GetEnumeratorUntyped() As IEnumerator _
Implements IEnumerable.GetEnumerator
Return Me.GetEnumerator()
End Function
''' <summary>
''' Returns an enumerator that iterates through each <see cref="TElement">TElement</see>.
''' </summary>
Public Function GetEnumerator() As IEnumerator(Of TElement) _
Implements IEnumerable(Of TElement).GetEnumerator
Return Me.elements.GetEnumerator()
End Function
End Class
Originally Posted by *Note*
For anyone that cares, they should note that I purposely did not declare TSource with a constraint of IEnumerable(Of TElement) so that you wouldn't have to do AsEnumerable calls on these collections. Just know the compiler is not checking the type safety of these collections.
Feel free to implement the functionality to refresh the iterator so that it can be reused. As it stands, it gets populated once.
-
Oct 14th, 2011, 04:39 PM
#2
Re: [.NET 3.5+] RecursiveIterator - Iterate Hierarchical Structures With One Class
Hehe, that's awesome, I wrote a very similar piece of code just today (in C# though) as I got tired of writing recursive functions just to loop through a hierarchical collection. I basically wrote the exact same thing except non generic (cause I only needed it for TreeNodes), but it works the same otherwise
I haven't tried yours but the code looks solid Should work fine, good job!
-
Oct 17th, 2011, 09:08 AM
#3
Re: [.NET 3.5+] RecursiveIterator - Iterate Hierarchical Structures With One Class
Originally Posted by NickThissen
Hehe, that's awesome, I wrote a very similar piece of code just today (in C# though) as I got tired of writing recursive functions just to loop through a hierarchical collection. I basically wrote the exact same thing except non generic (cause I only needed it for TreeNodes), but it works the same otherwise
I haven't tried yours but the code looks solid Should work fine, good job!
Thanks Nick. That's funny, I was writing something for TreeNodes too, and I looked at it and was like... this could just be a generic class.
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|