Results 1 to 3 of 3

Thread: [.NET 3.5+] RecursiveIterator - Iterate Hierarchical Structures With One Class

Hybrid View

  1. #1

    Thread Starter
    Master Of Orion ForumAccount's Avatar
    Join Date
    Jan 2009
    Location
    Canada
    Posts
    2,802

    [.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:

    1. 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
    2. 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
    3. 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
    Quote 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.

  2. #2
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    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!

  3. #3

    Thread Starter
    Master Of Orion ForumAccount's Avatar
    Join Date
    Jan 2009
    Location
    Canada
    Posts
    2,802

    Re: [.NET 3.5+] RecursiveIterator - Iterate Hierarchical Structures With One Class

    Quote Originally Posted by NickThissen View Post
    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
  •  



Click Here to Expand Forum to Full Width