2009-01-08 9 views
9

Hay 4 firmas sobrecargadas para Enumerable.SelectMany. Para hacerlo simple, no hacemos caso de las dos firmas con int argumento. Así que tenemos 2 firmas para SelectMany:¿Cómo C# compilador elige SelectMany cuando traduce la expresión LINQ?

public static IEnumerable<TResult> SelectMany<TSource, TResult>(
    this IEnumerable<TSource> source, 
    Func<TSource, IEnumerable<TResult>> selector 
) 

public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(
    this IEnumerable<TSource> source, 
    Func<TSource, IEnumerable<TCollection>> collectionSelector, 
    Func<TSource, TCollection, TResult> resultSelector 
) 

Mi pregunta es: ¿Cómo elegir compilador de C# SelectMany al traducir la expresión LINQ a la invocación de métodos de extensión?

Básicamente, si hay múltiples de en expresión LINQ, habrá SelectMany. Pero, parece que el compilador de C# solo elige la segunda firma. La primera firma nunca se usa.

 IEnumerable<int> en1 = Enumerable.Range(1, 3); 
     IEnumerable<double> en2 = new double[] { 1.0, 3.14 }; 

     IEnumerable<string> en3 = 
      from i1 in en1 
      from i2 in en2 
      select (i1 * i2).ToString(); 

     foreach (var i in en3) 
     { 
      Console.WriteLine(i); 
     } 

Con la ayuda del reflector, puedo ver que por encima de la expresión LINQ se traduce en

en1.SelectMany<int, double, string>(delegate (int i1) { 
     return en2; 
    }, delegate (int i1, double i2) { 
     double CS$0$0000 = i1 * i2return CS$0$0000.ToString(); 
    }) 

El ejemplo de arriba implica 3 tipos. Entonces, es razonable seleccionar la segunda firma de SelectMany. Sin embargo, para el ejemplo siguiente, solo está involucrado un tipo, aún selecciona la segunda firma.

 IEnumerable<int> en4 = 
      from i1 in en1 
      from i2 in Enumerable.Range(0, i1) 
      select i2; 

se traduce en:

en1.SelectMany<int, int, int>(delegate (int i1) { 
     return Enumerable.Range(0, i1); 
    }, delegate (int i1, int i2) { 
     return i2; 
    }) 

Por lo tanto, no puedo encontrar un caso que la expresión LINQ se traduce en la primera firma SelectMany. ¿Hay tal caso?

Si no se utiliza la primera firma SelectMany, entonces existe sólo porque se trata de BIND mónada en la programación funcional?

Tal vez la pregunta puede ser: ¿por qué tenemos 2 firmas de SelectMany?

Gracias.

Respuesta

5

Según el Spec C#, el compilador no generará una llamada a la sobrecarga de la primera versión de SelectMany. La primera versión de SelectMany es útil para aplanar una Lista de Listas en una sola lista plana.

public IEnumerable<string> Example(IEnumerable<IEnumerable<string>> enumerable) { 
    return enumerable.SelectMany(x => x); 
} 

No tiene un equivalente fuerte en una expresión de consulta.

, véase la Sección 7.15.2 del lenguaje C# de especificaciones para más información.

+1

Realmente ... ¿una función nula que devuelve algo y nadie lo comenta? –

5

¿por qué tenemos 2 firmas de SelectMany?

Así que puedo usar el primero en mi código.

var orders = Customers.SelectMany(c => c.Orders) 
+0

Seguramente. Eso es solo por conveniencia. –

+3

La primera firma de SelectMany es isomorfo al operador monádico "unen", escrito en Haskell >> =. Una razón por la que está incluido es para consolidar la base de LINQ en el marco bien establecido de las mónadas. Esa base hace que LINQ sea polimórficamente aplicable a todo tipo de cosas, como propagación del estado composable, excepciones, continuaciones, alternativas, etc .: todas las mónadas. Ver http://en.wikipedia.org/wiki/Monad_(functional_programming). –

Cuestiones relacionadas