简体   繁体   English

实现父子类层次结构

[英]Implement a parent-child class hierarchy

I'm finding it difficult to find a decent example on how to implement a parent-child hierarchy class. 我发现很难找到一个关于如何实现父子层次结构类的好例子。 I have a treeView control that I want to convert into a class hierarchy, adding extra data to each node and be able to easely iterate over each parent's nodes using IEnumerable. 我有一个treeView控件,我想将其转换为类层次结构,为每个节点添加额外的数据,并能够使用IEnumerable轻松迭​​代每个父节点。

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

I already have implemented the following piece of code but got stuck and don't really have a clue whether I am on the right track or not? 我已经实现了以下一段代码但是卡住了并且不知道我是否在正确的轨道上? How should I proceed to complete this ? 我该如何处理完成此操作?

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

You're mixing the responsibilities of the Node with the responsibilities of the collection. 您将Node的职责与集合的职责混合在一起。 See how you're setting the parent in the collection? 了解如何在集合中设置父级? It's not the collection that has a parent; 这不是拥有父母的集合; its the node. 它的节点。

I'd structure my nodes like thus: 我像这样构建我的节点:

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

And the NodeCollection would look like NodeCollection看起来像

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

and INodeCollection would be: 和INodeCollection将是:

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

The collection responsibilities are on the Child collection property of the Node. 集合职责在Node的Child集合属性上。 You can, of course, have node implement INodeCollection, but that's a matter of programming tastes. 当然,您可以让节点实现INodeCollection,但这是编程品味的问题。 I prefer to have the Children public property (its how the framework is designed). 我更喜欢拥有子公共财产(它的框架是如何设计的)。

With this implementation you don't need to implement a "GetChildren" method; 使用此实现,您不需要实现“GetChildren”方法; the public Children property provides them for all. 公共儿童财产为所有人提供。

我发现这篇博客文章在尝试解决同样的问题时非常有用。

If you want to separate the notion of a tree-like data structure from the specific data being stored, make it a general purpose container by making it generic. 如果要将树状数据结构的概念与存储的特定数据分开,请将其作为通用容器使其成为通用容器。

Also, if the tree has a single root, a treenode is itself a collection of treenodes, so (like any collection) the method for adding an item should be called Add . 此外,如果树具有单个根,则treenode本身是treenode的集合,因此(与任何集合一样)添加项的方法应该称为Add Making the child collection a separate object would only make sense if you often have collections of trees. 如果您经常拥有树集合,那么将子集合作为单独的对象才有意义。 This occurs in TreeViews in the Windows UI because the root of a TreeView contains multiple nodes rather than a single root treenode. 这发生在Windows UI中的TreeViews中,因为TreeView的根包含多个节点而不是单个根treenode。 However, in something like the XML or HTML DOM, there's always a single root, and so I think something simpler is appropriate. 但是,在XML或HTML DOM之类的东西中,总有一个根,所以我认为更简单的东西是合适的。

Finally, you don't need to implement the IEnumerable stuff with yield return - just forward to a standard container's implementation. 最后,您不需要使用yield return来实现IEnumerable内容 - 只需转发到标准容器的实现。

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

In fact you could make it implement IList<TreeNode<TValue>> and forward all the methods on to the list, with appropriate manipulation of the Parent property whenever adding/removing children. 实际上,您可以使它实现IList<TreeNode<TValue>>并将所有方法转发到列表中,并在添加/删除子项时适当地操作Parent属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM