在c#中填充树结构的优雅且可维护的方式

我有一棵树。

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

我想填充一个大的unit testing用途。 我真的想把东西放干。

为了说明的目的,我的树具有以下结构

家长,降序 
  孩子1,desc1
    孙子1,desc1 
  孩子2,desc2

您将如何以优雅可维护的方式填充树木?

我觉得这段代码非常重复且容易出错:

 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); 

编辑

我最终做了DSL方法:

演示测试就在这里 。

实施在这里 。

它使用构建器和简单的DSL。

  • 理想情况下,您需要一种方法将语言扩展为自定义类型的文字 。 C#没有这个,所以你必须找到另一种方法。

  • 您可以制作内部DSL ,通常具有流畅的界面

  • 遵循function构造XElement示例 。

  • 使用自定义解析器创建外部DSL 。 如果您仔细设计语言,解析器可以很容易。

  • 使用XML 。 基本上这是一种创建外部DSL并免费获得解析器的方法。

外部DSL选项很好,因为当你阅读它们时,你知道只有数据 ,而且不必担心理解代码结构。 此外,数据是文件,文件是数据。 这样可以通过更改文件轻松交换数据,并且更容易准备好文件更改历史记录。 最后,当非程序员提供数据时,外部DSL是好的。

这里的权衡是时间与价值。 您将拥有多少数据/更改频率/更改谁将是您必须回答的问题。

你可以编写一个带有状态的“TreeBuilder”来保存一些连接混乱:

 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() 

另一种方法是用一些简单的格式发明一个简单的配置文件/字符串。

嵌套结构可能是一个很好的选择。 不要暴露孩子名单的好主意。

 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 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 Children { get { return _children.AsReadOnly(); } set { _children.Clear(); _children.AddRange(value); } } private List _children = new List(); } 

您可以使用填充树的简单解析器编写树内容的简单XML表示。 以下将给出您在上面指定的结构。

  Parent  Child 1  Grandchild 1    Child 2   

我会将实现拆分为TreeClass和TreeNodeClass

树类将具有成员变量

 TreeNodeClass root 

用方法

 TreeNodeClass addAtRoot(data) 

返回它们刚刚创建的节点

TreeNodeClass还需要一个AddChild()方法,它还会返回刚刚添加的节点。

然后你可以做类似的事情

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

要么

使用这样的东西随机生成一棵树

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

主要思想是您要返回刚刚添加到树中的节点。

你可能也想让孩子知道它的父母,因为这有时候非常方便。