2010-10-03 9 views
40

Je me trouve souvent sur IEnumerables juste pour que je puisse retourner chaque résultat. Existe-t-il un moyen de compresser quelque chose comme ceciRendement Retour Beaucoup?

foreach (var subSelector in subSelectors) 
{ 
    foreach (var node in FindSingle(context, subSelector)) 
     yield return node; 
} 

Pour supprimer le foreach interne?

+0

cela a été demandé plusieurs fois et devrait être fusionné. chercher "yield multiple enumerable" – mafu

+0

@mafutrct: * Aucun résultat trouvé pour "yield multiple enumerable". * Pouvez-vous donner un exemple? –

+1

C'est ce que j'ai trouvé (accordé, avec une phrase de recherche différente): http://stackoverflow.com/questions/2055927/ienumerable-and-recursion-using-yield-return, http://stackoverflow.com/questions/ 1824934/rewrite-this-foreach-yield-to-a-linq-yield, http://stackoverflow.com/questions/1270024/nested-yield-return-with-ienumerable. Cependant, je n'ai pas trouvé la question que je cherchais qui expliquait exactement ce qui était demandé. Je me souviens aussi d'avoir moi-même posé cette question il y a quelque temps ... Je vais essayer de le retrouver dans ma liste de questions. – mafu

Répondre

14

Non, à moins que vous remplaciez complètement chaque yield return par une seule instruction return utilisant LINQ.

Par exemple:

return someSet 
    .Concat(someOtherSet.SelectMany(s => FindSingle(context, s)); 
3

Utilisation Enumerable.SelectMany:

return subSelectors.SelectMany(subselector => FindSingle(context, subSelector)); 

Cela ne fonctionne que si vous n'avez pas d'autres déclarations de retour de rendement dans votre méthode.

+10

Ouais ... mais alors tu ne peux plus rien céder après ça. – mpen

52

Il s'agit d'une fonctionnalité demandée fréquemment que C# ne prend pas en charge. Voir cet article Connect pour plus de détails:

http://connect.microsoft.com/VisualStudio/feedback/details/256934/yield-return-to-also-yield-collections

La syntaxe proposée est quelque chose comme:

public static IEnumerable<T> PreorderTraversal<T>(this BinaryTree<T> root) 
{ 
    if (root == null) yield break; 
    yield return root.Item; 
    yield foreach root.Left.PreorderTraversal(); 
    yield foreach root.Right.PreorderTraversal(); 
} 

Si vous êtes intéressé à jouer avec un langage C# -comme qui prend en charge cette fonction, prendre regarder cQ:

http://research.microsoft.com/en-us/um/cambridge/projects/comega/

Vous pouvez également lire cet article sur t il caractéristique par les implémenteurs de cQ:

http://research.microsoft.com/en-us/projects/specsharp/iterators.pdf

Si vous êtes intéressé par une langue non-C# -comme qui prend en charge cette fonctionnalité, jetez un oeil à la « rendement! » caractéristique de F #. (J'adore que le nom de la fonctionnalité soit "rendement!")

Même si vous n'êtes pas intéressé par les choses théoriques, il semble que vous ayez à faire face à cette situation comme un problème pratique. Vous devriez également lire l'article de Wes Dyer sur les techniques de faire efficacement ce genre d'itération imbriquée sans « rendement foreach »:

http://blogs.msdn.com/b/wesdyer/archive/2007/03/23/all-about-iterators.aspx

+0

Je pensais que Comega était déjà intégré dans C#, alors ils ont arrêté de le développer. Est-ce qu'il continue à apporter de nouvelles idées pour les futures versions C#? –

+1

@Joan: Certaines des idées de C-Omega ont été intégrées dans C#, certaines d'entre elles ne l'ont pas été. Que la recherche ou le développement continue, je ne sais pas. –

+7

Je pense que «céder chacun» sonnerait mieux. Merci d'avoir répondu! C'est intéressant. – mpen

2

Vous pouvez casser votre méthode en deux. Compte tenu de ces méthodes d'extension:

public static class MultiEnumerableExtensions { 
    public static IEnumerable<T> Pack<T>(this T item) { 
    yield return item; 
    } 
    public static IEnumerable<T> Flatten<T>(
    this IEnumerable<IEnumerable<T>> multiList) { 
    return multiList.SelectMany(x => x); 
    } 
} 

Et en utilisant de example Eric Lippert, il devient ceci:

public static class BinaryTreeExtensions { 
    public static IEnumerable<T> PreorderTraversal<T>(this BinaryTree<T> root) { 
    return PreorderTraversalMulti(root).Flatten(); 
    } 
    private static IEnumerable<IEnumerable<T>> PreorderTraversalMulti<T>(
    this BinaryTree<T> root) { 
    if (root == null) yield break; 
    yield return root.Item.Pack(); // this packs an item into an enumerable 
    yield return root.Left.PreorderTraversal(); 
    yield return root.Right.PreorderTraversal(); 
    } 
} 

La méthode intérieure donne enumerables de T au lieu de T, et la méthode externe a juste besoin d'aplatir ce résultat .

+0

On dirait beaucoup de frais généraux pour une petite quantité de sucre syntaxique. J'apprécie cependant la suggestion, c'est * intéressant *. – mpen

+0

Il y a probablement un seuil où il serait logique de l'utiliser. Probablement si vous avez plus de quelques "rendements" dans votre méthode. Je pense que l'exemple d'Eric, barely_, se qualifie. D'autres personnes pourraient penser autrement. –

0

Utilisez la puissance de Linq!

return subSelectors.SelectMany(s => FindSingle(context, s)); 
Questions connexes