Question

I'm banging my head on the desk for hours now, but it seems like I'm too stupid to implement a tree structure in C#.

  1. There are 2 types of nodes, I call them Node and NodeCollection.
  2. Both Node and NodeCollection can have a parent NodeCollection
  3. A NodeCollection can have a collection of child nodes, which are either Node or NodeCollection
  4. A Node cannot have any childs.
  5. A Node or NodeCollection without parent is considered to be the root node
  6. A Node has a value of any arbitary type, done with generics

    • NodeCollection
      • NodeCollection
        • Node
        • Node
        • NodeCollection
          • Node
          • NodeCollection
            • Node
            • Node
          • NodeCollection
        • Node

Is there a collection type from the BCL that serves this purpose? What I have so far:

public abstract class NodeBase {

    protected NodeCollection Parent { get; set; }

}

public class Node<T> : NodeBase {

    public string Key { get; set; }
    public T Value { get; set; }

}

public class NodeCollection : NodeBase {

    public ICollection<NodeBase> Children { get; set; }

}

This solution 'works', however I cannot just walk down the tree as NodeBase doesn't offer any childreen. I have to validate the type to find out if the child node is a NodeCollection, but if it is not, I can't properly cast the Node because it might be of unknown type.

Was it helpful?

Solution

The easiest option is to just have one Node class (rather than having two types of nodes, to separate leaves from non-leaf nodes), and to have your leaf nodes just have an empty collection of children rather than no collection at all.

If it's important to have the two types of nodes for other reasons, then you'd want to have both of them implement an interface that defines a collection of children, to which the leaf nodes will return an empty collection.

OTHER TIPS

For dealing with the Node not having a link to the collection, I use an abstract base class property called "HasChildren" to allow for checking, and ensure that the concrete Node implementation throws an exception if you forgot to check and tried accessing the children.

public abstract class NodeBase<T> {
  public abstract bool HasChildren { get; }
  public abstract ICollection<T> Children { get; set; }
}
public class NodeCollection<T> : NodeBase<T> {
  public override bool HasChildren { get { return true; } }
  public override ICollection<T> Children { get; set; }
}
public class Node<T> : NodeBase<T> {
  public override bool HasChildren { get { return false; } }
  public override ICollection<T> Children { get { throw new Exception("blah"); } set { throw new Exception("blah"); } }
}

I use an Add method concrete implementation from an abstract base class method to work out what to add, and how, for me this also takes care of collection sorting for adding items to NodeCollections, and enforces uniqueness if required.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top