2009-07-13 15 views
1

Je trouve difficile de trouver un exemple décent sur la façon d'implémenter une classe de hiérarchie parent-enfant. J'ai un contrôle treeView que je veux convertir en une hiérarchie de classes, en ajoutant des données supplémentaires à chaque nœud et être capable d'itérer facilement sur les nœuds de chaque parent en utilisant IEnumerable.Implémenter une hiérarchie de classe parent-enfant

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

Je l'ai déjà mis en œuvre le morceau de code suivant, mais est resté coincé et ne vraiment pas la moindre idée si je suis sur la bonne voie ou non? Comment devrais-je procéder pour compléter ceci?

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

Répondre

2

Vous mélangez les responsabilités du nœud avec les responsabilités de la collection. Voyez comment vous définissez le parent dans la collection? Ce n'est pas la collection qui a un parent; c'est le noeud.

je structurer mes nœuds comme ainsi:

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

Et le NodeCollection ressembleraient

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

et INodeCollection serait:

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

Les responsabilités de collecte sont en la propriété de collection Child du nœud. Vous pouvez, bien sûr, avoir un noeud implémentant INodeCollection, mais c'est une question de goût de programmation. Je préfère avoir la propriété publique Children (c'est ainsi que le framework est conçu).

Avec cette implémentation, vous n'avez pas besoin d'implémenter une méthode "GetChildren"; la propriété publique Children les fournit à tous.

1

Si vous voulez séparer la notion de structure de données arborescente des données spécifiques stockées, faites-en un conteneur général en le rendant générique.

De plus, si l'arborescence a une racine unique, un code d'entrée est lui-même une collection de droits d'entrée, donc (comme toute collection) la méthode d'ajout d'un élément doit être appelée Add. Faire de la collection enfant un objet séparé n'aurait de sens que si vous avez souvent des collections d'arbres. Cela se produit dans TreeViews dans l'interface utilisateur Windows car la racine d'un TreeView contient plusieurs nœuds plutôt qu'un seul code racine. Cependant, dans quelque chose comme le DOM XML ou HTML, il y a toujours une seule racine, et donc je pense que quelque chose de plus simple est approprié.

Enfin, vous n'avez pas besoin d'implémenter le IEnumerable avec yield return - juste en avant à l'implémentation d'un conteneur standard.

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

En fait, vous pouvez le faire mettre en œuvre IList<TreeNode<TValue>> et transmettre toutes les méthodes à la liste, avec une manipulation appropriée de la propriété Parent chaque fois que l'ajout/suppression des enfants.

Questions connexes