2012-09-03 3 views
2

Je travaille sur un programme dans lequel chaque élément peut contenir un tableau d'éléments (je fais un menu, qui a une structure arborescente)C# Programmation récursive avec les listes

actuellement j'ai les articles comme liste, au lieu d'un tableau, mais je ne me sens pas comme je l'utilise à son plein potentiel pour simplifier le code. J'ai choisi une liste sur un tableau standard car l'interface (.add, .remove, etc ...) a beaucoup de sens.

J'ai un code pour rechercher dans la structure et retourner le chemin du nom (c'est-à-dire Item.subitem.subsubitem.subsubsubitem). Ci-dessous est mon code:

public class Item 
{ 
                   //public Item[] subitem; <-- Array of Items 
    public List<Item> subitem;         // <-- List of Items 

    public Color itemColor = Color.FromArgb(50,50,200); 
    public Rectangle itemSize = new Rectangle(0,0,64,64); 
    public Bitmap itemBitmap = null; 
    public string itemName; 


    public string LocateItem(string searchName) 
    { 
     string tItemName = null; 

     //if the item name matches the search parameter, send it up) 
     if (itemName == searchName) 
     { 
      return itemName; 
     } 

     if (subitem != null) 
     { 

      //spiral down a level 
      foreach (Item tSearchItem in subitem) 
      { 
       tItemName = tSearchItem.LocateItem(searchName); 

       if (tItemName != null) 
        break; //exit for if item was found 
      } 
     } 


     //do name logic (use index numbers) 
     //if LocateItem of the subitems returned nothing and the current item is not a match, return null (not found) 
     if (tItemName == null && itemName != searchName) 
     { 
      return null; 
     } 

     //if it's not the item being searched for and the search item was found, change the string and return it up 
     if (tItemName != null && itemName != searchName) 
     { 
      tItemName.Insert(0, itemName + "."); //insert the parent name on the left --> TopItem.SubItem.SubSubItem.SubSubSubItem 
      return tItemName; 
     } 

     //default not found 
     return null; 
    } 


} 

Ma question est de savoir s'il y a un moyen plus facile de le faire avec des listes? J'ai fait des allers et retours dans ma tête pour savoir si je devais utiliser des listes ou juste un tableau. La seule raison pour laquelle j'ai une liste est que je n'ai pas besoin de faire du code pour redimensionner le tableau chaque fois que j'ajoute ou enlève un élément.

Répondre

2

Listes grand bruit. Je suggère cependant une variation sur votre définition. Essayez de créer votre classe comme ceci:

public class Item : List<Item> 
{ 
    public string Name; 
} 

Si vous faites Item Hériter de List<Item> vous faites automatiquement un arbre sans exiger le champ subitem.

Voilà ma version complète de votre classe:

public class Item : List<Item> 
{ 
    public string Name; 

    private List<Item> LocateItems(string searchName) 
    { 
     if (this.Name == searchName) 
      return (new [] { this }).ToList(); 

     var result = 
      this 
       .Select(s => s.LocateItems(searchName)) 
       .Where(x => x !=null && x.Count > 0) 
       .FirstOrDefault(); 

     if (result != null) 
      result.Add(this); 

     return result; 
    } 

    public string LocateItem(string searchName) 
    { 
     var items = this.LocateItems(searchName); 
     if (items == null) 
      return null; 
     else 
      return String.Join(".", items.Select(i => i.Name).Reverse()); 
    } 
} 

La méthode LocateItems retourne la liste des Item commençant par le Item qui correspondait et suivi par tous les parents Item instances jusqu'à et y compris la racine.

Je l'ai testé avec ce code:

var foos = new Item() { Name = "Foo" }; 
var bars = new Item() { Name = "Bar" }; 
var qazs = new Item() { Name = "Qaz" }; 
var wees = new Item() { Name = "Wee" }; 

foos.Add(bars); 
bars.Add(qazs); 
foos.Add(wees); 

Console.WriteLine(foos.LocateItem("Wee")); 
Console.WriteLine(foos.LocateItem("Qaz")); 
Console.WriteLine(foos.LocateItem("Bar")); 
Console.WriteLine(foos.LocateItem("Foo")); 

Et je suis arrivé à ces résultats:

Foo.Wee 
Foo.Bar.Qaz 
Foo.Bar 
Foo 
+0

il y a des quelques lignes que je ne suis pas au courant (je suis juste d'entrer dans des choses LINQ) . Que fait le FirstOrDefault? aussi, quel est le compte .Count? Aussi, je veux m'assurer que j'ai bien compris, il ajoute chaque niveau à une liste, qui est ensuite jointe à la fin avec des points dans l'ordre inverse. Enfin, la ligne .Select .Where fonctionne-t-elle comme une base de données SQL? –

+1

La méthode d'extension 'FirstOrDefault' renvoie la première valeur de la séquence, en ignorant le reste, mais retournera' null' si la séquence ne contient aucune valeur.Est-ce le cas, il est dit "donnez-moi le premier match sur la recherche, ou null s'il n'y a pas de matches". La méthode 'LocateItems' a été décrite dans ma réponse. Parce que le premier élément de la liste est le nœud feuille, il doit les inverser avant de rejoindre. Le 'Select' effectue une recherche récursive sur tous les sous-éléments et le' Where' filtre tous nos non-matches - tout comme une requête de base de données, mais en mémoire. – Enigmativity

+0

J'adopte votre type d'approche parce que je pense que cela semble plus facile, mais je suis curieux d'une chose. Vous avez dit que .Select effectue une recherche récursive sur tous les sous-éléments. Avec mon code d'origine, inclurait-il la recherche de tous les éléments de l'objet 'subitem', ou ne le ferait-il pas parce que c'est l'objet directement inclus dans la liste, pas le sous-élément lui-même? –

2

L'utilisation d'une liste dans ce cas est parfaitement acceptable. Un tableau ferait un meilleur choix si la performance était un problème - si c'est le cas, les tableaux sont légèrement plus rapides mais beaucoup moins flexibles que vous avez découvert. Une chose dont les gens ne parlent pas assez est que la simplicité est une bonne base pour structurer le code. S'il est plus simple d'écrire et de maintenir en utilisant des listes que des tableaux, alors (toutes choses égales par ailleurs) en utilisant des listes est parfaitement correct.

1

Je suggère des listes. Puisque l'ajout/suppression d'éléments à un tableau réalloue la mémoire, pour une collection dynamique d'éléments (ce que je suppose être votre cas), les listes ont généralement une meilleure performance globale. Vous pouvez jeter un oeil à:

Array versus List<T>: When to use which?