1

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

3 回答 3

2

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

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

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”方法;公共儿童财产为所有人提供。

于 2009-07-13T16:33:16.463 回答
1

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

于 2009-07-14T18:00:57.950 回答
1

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

此外,如果树只有一个根,则树节点本身就是树节点的集合,因此(与任何集合一样)应该调用添加项目的方法Add。仅当您经常拥有树的集合时,才使子集合成为一个单独的对象才有意义。这发生在 Windows UI 中的 TreeView 中,因为 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在添加/删除子项时对属性进行适当的操作。

于 2009-07-14T19:07:31.230 回答