实现父子类层次结构

时间:2009-07-13 16:13:54

标签: c# collections .net-2.0 parent-child

我发现很难找到一个关于如何实现父子层次结构类的好例子。 我有一个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 个答案:

答案 0 :(得分:2)

您将Node的职责与集合的职责混合在一起。了解如何在集合中设置父级?这不是拥有父母的集合;它的节点。

我会像这样构建我的节点:

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

集合职责在Node的Child集合属性上。当然,您可以让节点实现INodeCollection,但这是编程品味的问题。我更喜欢拥有子公共财产(它的框架是如何设计的)。

使用此实现,您不需要实现“GetChildren”方法;公共儿童财产为所有人提供。

答案 1 :(得分:1)

我发现this blog article在尝试解决同样的问题时非常有用。

答案 2 :(得分:1)

如果要将树状数据结构的概念与存储的特定数据分开,请将其作为通用容器使其成为通用容器。

此外,如果树具有单个根,则treenode本身是treenode的集合,因此(与任何集合一样)添加项的方法应称为Add。如果您经常拥有树集合,那么将子集合作为单独的对象才有意义。这发生在Windows UI中的TreeViews中,因为TreeView的根包含多个节点而不是单个根treenode。但是,在像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属性。