An elegant and maintainable way to populate tree structures in C #

I have a tree.

class TreeNode { public TreeNode(string name, string description) { Name = name; Description = description; } string Name { get; set; } string Description { get; set; } public List<TreeNode> Children = new List<TreeNode>(); } 

I would like to fill out a large one for testing purposes. I really would like to keep the material DRY.

Say for illustrative purposes, my tree has the following structure

  Parent, desc 
   Child 1, desc1
     Grandchild 1, desc1 
   Child 2, desc2

How would you start filling a tree in an elegant, convenient way?

I find this code quite repetitive and error prone:

 var parent = new TreeNode("Parent", "desc"); var child1 = new TreeNode("Child 1", "desc1"); var child2 = new TreeNode("Child 2", "desc2"); var grandchild1 = new TreeNode("Grandchild 1", "desc1"); parent.Children.Add(child1); parent.Children.Add(child2); child1.Children.Add(grandchild1); 

EDIT

I ended up making a DSL approach:

The test demo lives here .

The implementation is here .

It uses a builder and simple DSL.

+2
source share
5 answers
  • Ideally, you want to extend the language to user-type literals . C # doesn't have this, so you need to find a different approach.

  • You can create an internal DSL , usually with a free interface .

  • Follow XElement example functional design .

  • Create an external DSL using a custom analyzer. If you carefully design the language, a parser can be easy.

  • Use XML . Basically, this is a way to create an external DSL and get a parser for free.

The external parameters of DSL are good, because when you read them, you know that there is only data, and you don’t have to worry about understanding code constructs. In addition, the data is a file, and the file is data. This makes it easy to share data by modifying files and simplifying preparation for file change history. Finally, external DSL is also good when a non-programmer delivers data.

The trade-off here is time versus cost. How much data you will have / how often it will change / who will change it , there will be questions that you must answer.

+2
source

You can write "TreeBuilder" with a state to preserve some mess of connections:

 TreeBuilder builder = new TreeBuilder(); builder.AddNode("Parent", "desc"); // Adds a node, and sets the cursor to it builder.AddLeaf("Child 1", "desc1"); // Adds a node and leaves the cursor at the Parent builder.AddNode("Child 2", "desc2"); builder.AddLeaf("Grandchild 1", "desc1"); builder.Up(); // Moves the cursor to the parent builder.AddNode("Child 3", "desc3"); root = builder.GetRoot() 

Another way is to create a simple configuration file / line with some simple format.

+3
source

Nesting may be a good option here. It’s a good idea not to show the list of children too.

 class Program { static void Main(string[] args) { var parent = new TreeNode( "Parent", "desc", new TreeNode[] { new TreeNode( "Child 1", "desc1", new TreeNode[] { new TreeNode( "Grandchild 1", "desc1" ) } ), new TreeNode( "Child 2", "desc2" ) } ); } } class TreeNode { public TreeNode(string name, string description, IEnumerable<TreeNode> children) : this(name, description) { _children.AddRange(children); } public TreeNode(string name, string description) { Name = name; Description = description; } public string Name { get; set; } public string Description { get; set; } public IEnumerable<TreeNode> Children { get { return _children.AsReadOnly(); } set { _children.Clear(); _children.AddRange(value); } } private List<TreeNode> _children = new List<TreeNode>(); } 
+2
source

You can write a simple XML representation of the contents of the tree with a simple parser that populates the tree. Below is the structure indicated above.

 <Node description="desc"> Parent <Node description="desc1"> Child 1 <Node description="desc1"> Grandchild 1 </Node> </Node> <Node description="desc2"> Child 2 </Node> </Node> 
+1
source

I would split the implementation into TreeClass and TreeNodeClass

There will be member variables in the tree class

 TreeNodeClass root 

with methods

 TreeNodeClass addAtRoot(data) 

What returns the node just created

TreeNodeClass also needs the AddChild () method, which also returns only node.

Then you can do something like

 addAtRoot(rootData).AddChild(childData).AddChild(grandchildData); 

or

Use something like this to randomly generate a tree

 AddRecursively(TreeNodeClass root) { numChildren = SomeRandomNumber; While(numChildren > 0) { CTreeNodeClass newnode = root.AddChild(SomeRandomData); AddRecursively(newnode); } } 

The main idea is that you want to return the node that you just added to the tree.

You may also want the child to know his parent, as this can sometimes be very helpful.

+1
source

Source: https://habr.com/ru/post/1397148/


All Articles