2010-12-10 47 views
1

J'ai écrit le code suivant pour récupérer les StructureId s d'un IEnumerable<Structure>:Puis-je utiliser l'action <T> pour effectuer une recherche récursive et renvoyer un IEnumerable d'une propriété spécifiée?

Action<Structure> recurse = null; 
List<int> structureIds = new List<int>(); 
recurse = (r) => 
{ 
    structureIds.Add(r.StructureId); 
    r.Children.ForEach(recurse); 
}; 

IEnumerable<Structure> structures = GetStructures(); 
structures.ForEach(recurse); 

Je voudrais vraiment faire de ce générique je peux donc l'utiliser avec tout IEnumerable, à savoir quelque chose comme:

public static IEnumerable<TType> GetPropertyValues<TType, TPropertyType>(
    this IEnumerable<TType> this, <Property Declaration>) 
{ 
    // Generic version of the above code? 
} 

Est-ce que cela peut être fait?

+0

Peut-il être fait? Oui. Quelqu'un pourrait-il encore comprendre votre code? – Steven

+0

@Steven - Voir la réponse acceptée. C'est très compréhensible et fonctionne avec brio. – GenericTypeTea

Répondre

5

Action n'est pas très Linq'ish. Que diriez-vous de Func à la place? (Code non testé)

public static IEnumerable<TProp> RecurseSelect<TSource, TProp>(
    this IEnumerable<TSource> source, 
    Func<TSource, TProp> propertySelector, 
    Func<TSource, IEnumerable<TSource>> childrenSelector 
) 
{ 
    foreach(TSource x in source) 
    { 
    yield return propertySelector(x); 
    IEnumerable<TSource> children = childrenSelector(x); 
    IEnumerable<TProp> values = children.RecurseSelect(propertySelector, childrenSelector); 
    foreach(TProp y in values) 
    { 
     yield return y; 
    } 
    } 
} 

Et puis

IEnumerable<Structure> structures = GetStructures(); 
IEnumerable<int> structureIds = structures.RecurseSelect(
    s => s.StructureId, 
    s => s.Children); 
+0

Absolument parfait (tel quel). Je savais que cela pourrait être fait, merci :). – GenericTypeTea

1

Votre problème est que vous n'ajoutez pas chaque élément à une liste, vous ajoutez la propriété a de chaque élément. Cette propriété ne sera disponible que pour un Structure, et aucun autre type avec lequel vous pourriez réutiliser le code.

Vous n'avez pas non plus de mécanisme pour obtenir les enfants de vos autres classes. (la propriété r.Children que vous utilisez). Vos deux solutions consisteraient à utiliser des interfaces (c'est-à-dire, définir IHasChildren et IGetProperty) qui pourraient être utilisées comme types de base pour un algorithme simple, ou vous pourriez transmettre des fonctions à votre méthode qui permettent de calculer plus librement . Par exemple, votre signature de méthode doit être la suivante:

public static IEnumerable<TPropertyType> GetPropertyValues<TType, TPropertyType> 
     (this IEnumerable<TType> rootItem, Func<TType, IEnumerable<TType>> getChildren, Func<TType, TPropertyType> getIdValue) 

... mais cela ne va pas être très joli!