• Blog
  • Info Support
  • Career
  • Training
  • International Group
  • Info Support
  • Blog
  • Career
  • Training
  • International Group
  • Search
logo InfoSupport
  • Latest blogs
  • Popular blogs
  • Experts
      • All
      • Bloggers
      • Speakers
  • Meet us
  • About us
    • nl
    • en
    • .NET
    • 3D printing
    • Advanced Analytics
    • Agile
    • Akka
    • Alexa
    • Algorithms
    • Api's
    • Architectuur
    • Artificial Intelligence
    • ATDD
    • Augmented Reality
    • AWS
    • Azure
    • Big Data
    • Blockchain
    • Business Intelligence
    • Chatbots
    • Cloud
    • Code Combat
    • Cognitive Services
    • Communicatie
    • Containers
    • Continuous Delivery
    • CQRS
    • Cyber Security
    • Dapr
    • Data
    • Data & Analystics
    • Data Science
    • Data Warehousing
    • Databricks
    • DataOps
    • Developers life
    • DevOps
    • Digital Days
    • Digital Twin
    • Docker
    • eHealth
    • Enterprise Architecture
    • Event Sourcing
    • Hacking
    • Infrastructure & Hosting
    • Innovatie
    • Integration
    • Internet of Things
    • Java
    • Machine Learning
    • Microservices
    • Microsoft
    • Microsoft Bot Framework
    • Microsoft Data Platform
    • Mobile Development
    • Mutation Testing
    • Open source
    • Pepper
    • Power BI
    • Privacy & Ethiek
    • Python
    • Quality Assistance & Test
    • Quality Assurance & Test
    • Requirements Management
    • Scala
    • Scratch
    • Security
    • SharePoint
    • Software Architecture
    • Software development
    • Software Factory
    • SQL Server
    • SSL
    • Start-up
    • Startup thinking
    • Stryker
    • Test Quality
    • Testing
    • TLS
    • TypeScript
    • Various
    • Web Development
    • Web-scale IT
    • Xamarin
    • All
    • Bloggers
    • Speakers
Home » More fun with Extension Methods and Lambda’s: Creating a generic tree visitor
  • More fun with Extension Methods and Lambda's: Creating a generic tree visitor

    • By Frank Bakker
    • .NET 14 years ago
    • .NET 0 comments
    • .NET .NET
    More fun with Extension Methods and Lambda's: Creating a generic tree visitor

    One of the most common data structures in Software Engineering is the tree. In the .Net framework for instance you can find them in the file system, the controls of a win-forms application, nodes in an XML document etc.

    A common task with trees is to traverse all nodes to look for a specific node or perform an operation on each node. This is usually called visiting. Many tree like data structures unfortunately don't have a built-in visiting mechanism, so we usually need to build this ourselves with something like a recursive method.

    What I wanted to do is to write an generic implementation to visit any recursive tree. While the basic algorithm to do this is not very complicated, there are two factors that make it difficult to make the implementation generic, the first is that the type of the nodes is different for each tree, the second is that the way to obtain the children of a node is different for each tree. C# 2.0 solved the first problem with generics, and now C# 3.0 has solved the second problem with Lambda expressions.

    A generic tree visitor should allow me to iterate all nodes as a single Enumerable<T>, which of course has the advantage that it can be used it a foreach or a Linq query. So the code I Would like to write to visit a tree would be something like the following:

    foreach (var node in root.VisitDepthFirst(n => n.GetChildNodes()))

    {

        // …

    }

     

    Where VisitDepthFirst is a generic extension method that operates on the root object. The extension method receives a Lambda expression that is used on a node to retrieve all it's children. For most trees this is a single property or method which return a collection of all child objects.

    See the implementation of the depth first visitor below

    static public IEnumerable<T> VisitDepthFirst<T>(this T root, Func<T, IEnumerable<T>> childSelector)

    {

        if (root == null) yield break;

        // we return the root

        yield return root;

        // then we do a recursive Depth first search of all children and yield each item we find

        foreach (var child in childSelector(root).SelectMany(c => c.VisitDepthFirst(childSelector)))

        {

           yield return child;

        }

    }

    When testing this in a Win-forms application I first tried the following

    foreach (var control in groupBox2.VisitDepthFirst(c => c.Controls))

    {

        control.Text = "Done";

    }

    This however leads to a a few compiler errors. The first is has to do with the fact that groupBox2 is of type GroupBox and not Control therefore the compiler expects the Lambda expression to return IEnumerable<GroupBox > instead of IEnumerable<Control>. The second problem is that the ControlsCollection does not implement a generic IEnumerable<> at all but in stead only the non-generic IEnumerable.

    To fix this I had to be a bit more explicit about the type of the node and use Linq's Cast<>() Extension method to convert the Controls collection to IEnumerable<Control>. The following is the correct way to do this.

    foreach (var control in groupBox2.VisitDepthFirst<Control>(c => c.Controls.Cast<Control>()))
    {
        control.Text = "Done";
    }

    Likewise visiting all nodes in an xml document is done as follows

    foreach (XmlNode childNode in xmlDocument.VisitDepthFirst<XmlNode>(n => n.ChildNodes.Cast<XmlNode>()))

    {

    }

    To be complete I also created a breath-first search. I don't know if I'll ever use it, but why not.

    static public IEnumerable<T> VisitBreathFirst<T>(this T root, Func<T, IEnumerable<T>> childSelector)

    {

        if (root == null) yield break;

        Queue<T> open = new Queue<T>();

        // the open queue contains the items that have already been visited, but who's children have not

        yield return root;

        open.Enqueue(root);

        while (open.Count >0)

        {

            T item = open.Dequeue();

            foreach (T child in childSelector(item))

            {

                yield return child;

                open.Enqueue(child);

            }

        }

    }

    While I think this is a nice example of generics and Lambda's, I have to be hones that I'm not quite sure if this should have been implemented as Extension Methods. The problem here is that these extension methods operate on any object without restriction, which makes them pop up in intellisense all over the place once you add the using directive. The alternative ofcource is to make it a normal static method and loose the syntactic sugar of extension methods. 

    Any comments on that one?

    Share this

Frank Bakker

View profile

Related IT training

Go to training website

Related Consultancy solutions

Go to infosupport.com

Related blogs

  • Building a custom Kubernetes operator in C#

    Building a custom Kubernetes operator in C# Willem Meints - 2 months ago

  • Transform SpecFlow Table Column

    Transform SpecFlow Table Column Ronald Bosma - 4 months ago

  • Building a passwordless login flow with WebAuthn and th…

    Building a passwordless login flow with WebAuthn and th… Willem Meints - 7 months ago

Data Discovery Channel

  • Explainable AI - Break open the blackbox

  • Toekomstvaste microservice data architecturen

  • Modern Data Platform

Nieuwsbrief

* verplichte velden

Contact

  • Head office NL
  • Kruisboog 42
  • 3905 TG Veenendaal
  • T +31 318 552020
  • Call
  • Mail
  • Directions
  • Head office BE
  • Generaal De Wittelaan 17
  • bus 30 2800 Mechelen
  • T +32 15 286370
  • Call
  • Mail
  • Directions

Follow us

  • Twitter
  • Facebook
  • Linkedin
  • Youtube

Newsletter

Sign in

Extra

  • Media Library
  • Disclaimer
  • Algemene voorwaarden
  • ISHBS Webmail
  • Extranet
Beheer cookie toestemming
Deze website maakt gebruik van Functionele en Analytische cookies voor website optimalisatie en statistieken.
Functioneel Always active
De technische opslag of toegang is strikt noodzakelijk voor het legitieme doel het gebruik mogelijk te maken van een specifieke dienst waarom de abonnee of gebruiker uitdrukkelijk heeft gevraagd, of met als enig doel de uitvoering van de transmissie van een communicatie over een elektronisch communicatienetwerk.
Voorkeuren
De technische opslag of toegang is noodzakelijk voor het legitieme doel voorkeuren op te slaan die niet door de abonnee of gebruiker zijn aangevraagd.
Statistieken
De technische opslag of toegang die uitsluitend voor statistische doeleinden wordt gebruikt. De technische opslag of toegang die uitsluitend wordt gebruikt voor anonieme statistische doeleinden. Zonder dagvaarding, vrijwillige naleving door uw Internet Service Provider, of aanvullende gegevens van een derde partij, kan informatie die alleen voor dit doel wordt opgeslagen of opgehaald gewoonlijk niet worden gebruikt om je te identificeren.
Marketing
De technische opslag of toegang is nodig om gebruikersprofielen op te stellen voor het verzenden van reclame, of om de gebruiker op een website of over verschillende websites te volgen voor soortgelijke marketingdoeleinden.
Manage options Manage services Manage vendors Read more about these purposes
Voorkeuren
{title} {title} {title}