2010-10-03 11 views
40

Me encuentro empeñando demasiado en IEnumerables solo para poder devolver cada resultado. ¿Hay alguna manera de comprimir algo como estorendimiento rendimiento muchos?

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

Para quitar el foreach interno?

+0

esto se ha preguntado muchas veces y debería combinarse. buscar "rendimiento múltiple enumerable" – mafu

+0

@mafutrct: * No se encontraron resultados para "rendimiento enumerable múltiple". * ¿Puedes dar un ejemplo? –

+1

Esto es lo que encontré (concedido, con una frase de búsqueda diferente): 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. Sin embargo, no encontré la pregunta que estaba buscando que explicara exactamente lo que se solicitaba. También recuerdo haberme preguntado esto hace un tiempo ... Trataré de buscarlo en mi lista Q. – mafu

Respuesta

14

No, no hay, a menos que reemplace completamente cada yield return con una sola instrucción return usando LINQ.

Por ejemplo:

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

Uso Enumerable.SelectMany:

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

Esto sólo funciona si no tienen otras declaraciones de retorno de rendimiento en su método.

+10

Sí ... pero luego no puedes rendir nada más después de ese punto. – mpen

52

Esta es una característica requerida con frecuencia que C# no es compatible. Ver este artículo Connect para más detalles:

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

La sintaxis propuesta es generalmente algo como:

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 usted está interesado en jugar con un lenguaje C# -como compatible con esta característica, tomar una vistazo a Cω:

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

también podría querer leer este documento sobre t a función por los ejecutores de Cω:

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

Si está interesado en un idioma # -como no-C compatible con esta característica, echar un vistazo al "rendimiento" característica de F #. (Me encanta que el nombre de la función sea "yield!")

Incluso si no está interesado en lo teórico, parece que se enfrenta a esta situación como un problema práctico. También debe leer el artículo de Wes Dyer sobre técnicas para hacer eficientemente este tipo de iteración anidada sin "foreach rendimiento":

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

+0

Pensé que Comega ya está integrado en C#, por lo que dejaron de desarrollarlo. ¿Todavía continúa trayendo nuevas ideas para futuras versiones de C#? –

+1

@Joan: Algunas de las ideas de C-omega se han integrado en C#, algunas de ellas no. Si cualquier investigación o desarrollo continúa en él, no lo sé. –

+7

Creo que solo 'rendimiento cada' sonaría mejor. ¡Gracias por la respuesta! Esto es interesante. – mpen

2

Usted puede romper su método en dos. Dados estos métodos de extensión:

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

Y utilizando de example Eric Lippert, se convierte en esto:

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

El método interno produce enumerables de T en lugar de Ts, y el método exterior sólo necesita para aplanar este resultado .

+0

Parece mucho sobrecarga para una pequeña cantidad de azúcar sintáctico. Aunque aprecio la sugerencia, * es * interesante. – mpen

+0

Probablemente exista un umbral en el que tendría sentido usarlo. Probablemente si tiene más de unos pocos "rendimientos foreach" en su método. Creo que el ejemplo de Eric _apenas_ califica. Otras personas pueden pensar lo contrario. –

0

¡Utilice la potencia de Linq!

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