Реализуйте иерархию родительско-дочерних классов - PullRequest
1 голос
/ 13 июля 2009

Мне трудно найти достойный пример того, как реализовать класс иерархии родитель-потомок. У меня есть элемент управления treeView, который я хочу преобразовать в иерархию классов, добавив дополнительные данные к каждому узлу и могу легко перебирать узлы каждого родителя с помощью IEnumerable.

public IEnumerable<Node> GetAllChildsFromParent(Node parent)
{
    foreach (Node node in parent.NodeChildsCollection)
    {
        yield return node;
    }
}

Я уже реализовал следующий фрагмент кода, но застрял и не очень есть подсказка, нахожусь ли я на правильном пути или нет? Как мне поступить, чтобы завершить это?

public class NodeChildsCollection : IEnumerable<Node>
{
    IList<Node> nodeCollection = new List<Node>();
    Node parent;

    public Node Parent
    {
        get { return parent; }
        set { parent = value; }
    }

    public NodeChildsCollection()
    {
    }


    public void AddNode(Node parent, Node child)
    {
        this.parent = parent;
        nodeCollection.Add(child);
    }

    #region IEnumerable<Node> Members

    public IEnumerator<Node> GetEnumerator()
    {
        foreach (Node node in nodeCollection)
        {
            yield return node;
        }
    }

    #endregion

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion
}

public class Node
{

    NodeChildsCollection nodeChildsCollection = new NodeChildsCollection();

    public Node Parent
    {
        get { return nodeChildsCollection.Parent; }
        set { nodeChildsCollection.Parent = value; }
    }


    public void AddChild(Node child)
    {
        nodeChildsCollection.AddNode(this, child);
    }
}

Ответы [ 3 ]

2 голосов
/ 13 июля 2009

Вы смешиваете обязанности Узла с обязанностями коллекции. Видите, как вы устанавливаете родителя в коллекции? Это не коллекция, у которой есть родитель; это узел.

Я бы структурировал свои узлы так:

public class Node
{
  public Node Parent {get;set;} // null for roots

  public NodeCollection Children {get; private set;}

  public Node() 
  { 
    Children = new NodeCollection(); 
    Children.ChildAdded += ChildAdded;
    Children.ChildRemoved += ChildRemoved;
  };
  private void ChildAdded(object sender, NodeEvent args)
  {
    if(args.Child.Parent != null)
      throw new ParentNotDeadYetAdoptionException("Child already has parent");
    args.Child.Parent = this;
  }
  private void ChildRemoved(object sender, NodeEvent args)
  {
    args.Child.Parent = null;
  }
}

И NodeCollection будет выглядеть как

public class NodeCollection : INodeCollection {/*...*/}

и INodeCollection будет:

public interface INodeColleciton : IList<Node>
{
  event EventHandler<NodeEvent> ChildAdded;
  event EventHandler<NodeEvent> ChildRemoved;
}

Обязанности по сбору принадлежат дочерней коллекции узла. Вы можете, конечно, иметь узел, реализующий INodeCollection, но это вопрос вкусов программирования. Я предпочитаю иметь детскую общественную собственность (это как структура разработана).

В этой реализации вам не нужно реализовывать метод "GetChildren"; государственная детская собственность предоставляет их всем.

1 голос
/ 14 июля 2009

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

Кроме того, если дерево имеет один корень, сам триод является набором триодов, поэтому (как и любая коллекция) метод добавления элемента должен называться Add. Делать дочернюю коллекцию отдельным объектом имеет смысл, только если у вас часто есть коллекции деревьев. Это происходит в TreeViews в пользовательском интерфейсе Windows, потому что корень TreeView содержит несколько узлов, а не один корневой триод. Однако в чем-то вроде XML или HTML DOM всегда есть один корень, и поэтому я думаю, что подходит что-то более простое.

Наконец, вам не нужно реализовывать IEnumerable с помощью yield return - просто перейдите к реализации стандартного контейнера.

public class TreeNode<TValue> : IEnumerable<TreeNode<TValue>>
{
    private List<TreeNode<TValue>> _children = new List<TreeNode<TValue>>();

    public TreeNode<TValue> Parent { get; private set; }

    public void Add(TreeNode<TValue> child)
    {
        _children.Add(child);
        child.Parent = this;
    }

    public void Remove(TreeNode<TValue> child)
    {
        _children.Remove(child);
        child.Parent = null;
    }

    public IEnumerator<TreeNode<TValue>> GetEnumerator()
    {
        return _children.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return _children.GetEnumerator();
    }      
}

Фактически, вы можете заставить его реализовать IList<TreeNode<TValue>> и перенаправить все методы в список с соответствующей манипуляцией со свойством Parent при добавлении / удалении дочерних элементов.

1 голос
/ 14 июля 2009

Я нашел эту статью в блоге весьма полезной при попытке решить ту же проблему.

...