2009-02-18 2 views
2

У меня есть дерево.Элегантный и удобный способ заселения древовидных структур в C#

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

Я хотел бы заполнить большую для целей тестирования. Я действительно хотел бы сохранить материал СУХОЙ.

Say для иллюстрации моего дерево имеет следующую структуру

 
Parent,desc 
    Child 1, desc1 
    Grandchild 1, desc1 
    Child 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); 

EDIT

я в конечном итоге делает DSL подход:

Демонстрационный test lives here.

implementation is here.

В нем используется строитель и простая DSL.

ответ

2
  • В идеале вы хотите способ расширить язык литералов пользовательских типов. У C# этого нет, поэтому вам нужно найти другой подход.

  • Вы можете изготовить внутреннего DSL, как правило, с беглым интерфейсом.

  • Следуйте за XElement example of functional construction.

  • Создайте внешний DSL с помощью настраиваемого анализатора. Если вы тщательно разработаете язык, синтаксический анализатор может быть простым.

  • XML. В принципе, это способ создать внешний DSL и бесплатно получить парсер.

Внешние параметры DSL хороши, потому что, когда вы читаете их, вы знаете, есть только данные, и не придется беспокоиться о том, чтобы смысл кодовых конструкций. Кроме того, данные являются файлом, а файл - данными. Это позволяет легко обмениваться данными, изменяя файлы и упрощая подготовку к истории изменений файлов. Наконец, и внешний DSL хорош, когда не-программист будет поставлять данные.

Компромисс здесь - время против стоимости. Сколько данных у вас будет/как часто это изменится/кто его изменит - это вопросы, на которые вы должны ответить.

3

Вы можете написать «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() 

Другим способом изобрести простую конфигурационный файл/строку с каким-нибудь простым форматом.

1

Вы можете написать простое XML-представление содержимого дерева с помощью простого парсера, который заполняет дерево. Ниже приведена структура, указанная выше.

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

+1. Это одна из единственных вещей, для которых на самом деле хорошо подходит XML, поскольку это в основном читаемая человеком форма дерева. – configurator

+0

Большое преимущество использования XML над DSL, встроенным в строку, - это множество инструментов, которые его окружают. – ICR

2

Вложенная конструкция может быть хорошим вариантом здесь. Хорошая идея не показывать список детей тоже.

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>(); 
} 
1

Я бы разделить реализацию в TreeClass и TreeNodeClass

Класс Дерево будет иметь переменные-члены

TreeNodeClass root 

с методами

TreeNodeClass addAtRoot(data) 

Который возвращает узел они только что создали

The TreeNodeClass Также необходим AddCh ild(), который также вернет только что добавленный узел.

Затем вы можете сделать что-то вроде

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

или

использовать что-то вроде этого, чтобы случайным образом на дереве

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

Основная идея заключается в том, что вы хотите, чтобы вернуть узел, который вы просто добавили в дерево.

Возможно, вы также захотите, чтобы ребенок знал своего родителя, поскольку иногда это может пригодиться.