2009-07-13 166 views
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); 
    } 
} 

回答

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

收集職責是對節點的子集合屬性。當然,您可以讓節點實現INodeCollection,但這是編程風格的問題。我更喜歡擁有Children公共屬性(它的框架是如何設計的)。

使用此實現,您不需要實施「GetChildren」方法;公共兒童財產爲所有人提供。

1

如果要將樹形數據結構的概念與要存儲的特定數據分開,請將其作爲通用容器。另外,如果樹有單根,treenode本身就是treenodes的集合,所以(與任何集合一樣)添加項目的方法應稱爲Add。如果您經常收集樹木,則將子集合作爲單獨的對象纔有意義。這發生在Windows UI中的TreeView中,因爲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財產的適當操作。