【问题标题】:Implement a parent-child class hierarchy实现父子类层次结构
【发布时间】:2009-07-13 16:13:54
【问题描述】:

我发现很难找到一个关于如何实现父子层次类的体面示例。 我有一个 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);
    }
}

【问题讨论】:

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


    【解决方案1】:

    您将节点的职责与集合的职责混合在一起。看看你是如何在集合中设置父级的?不是有父集合的集合;它的节点。

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

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

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

    使用此实现,您无需实现“GetChildren”方法;公共 Children 财产为所有人提供它们。

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

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

        此外,如果树只有一个根,则树节点本身就是树节点的集合,因此(与任何集合一样)添加项的方法应称为Add。仅当您经常拥有树的集合时,才使子集合成为一个单独的对象才有意义。这发生在 Windows UI 中的 TreeView 中,因为 TreeView 的根包含多个节点而不是单个根树节点。但是,在 XML 或 HTML DOM 之类的东西中,总是有一个根,所以我认为更简单的东西是合适的。

        最后,您不需要使用 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();
            }      
        }
        

        事实上,您可以让它实现IList&lt;TreeNode&lt;TValue&gt;&gt; 并将所有方法转发到列表中,并在添加/删除子项时适当地操作Parent 属性。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-04-23
          • 1970-01-01
          • 2015-05-14
          • 1970-01-01
          • 1970-01-01
          • 2020-05-29
          • 1970-01-01
          相关资源
          最近更新 更多