Pergunta

Existem 4 assinaturas sobrecarregados para Enumerable.SelectMany. Para tornar mais simples, ignoramos as duas assinaturas com int argumento. Então temos 2 assinaturas para SelectMany:

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
)

A minha pergunta é: como C # compilador escolher SelectMany ao traduzir expressão LINQ para invocação de método de extensão

Basicamente, se houver vários na expressão LINQ, haverá SelectMany. Mas, parece que compilador C # só escolher a segunda assinatura. A primeira assinatura nunca é utilizado.

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

Com a ajuda do refletor, eu posso ver aquela expressão LINQ se traduziu em

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

O exemplo acima envolve 3 tipos. Assim, é razoável para selecionar a segunda assinatura SelectMany. No entanto, para seguir exemplo, apenas um tipo está envolvido, ele ainda seleciona a segunda assinatura.

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

Ele é traduzido em:

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

Então, eu não posso encontrar um caso que a expressão LINQ é traduzido para a primeira assinatura SelectMany. Existe tal caso?

Se a primeira assinatura SelectMany não é usado, então ele existe apenas porque é BIND de mônada na programação funcional?

Talvez a pergunta pode ser:? Por que temos 2 assinaturas de SelectMany

Graças.

Foi útil?

Solução

De acordo com a C # Spec, o compilador não irá gerar uma chamada de sobrecarga para a primeira versão do SelectMany. A primeira versão do SelectMany é útil para aplainar uma lista de listas em uma única lista plana.

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

Não tem um forte equivalente em uma expressão de consulta.

Veja a Seção 7.15.2 da Linguagem C # Spec para mais informações.

Outras dicas

Por que temos 2 assinaturas de SelectMany?

Então, eu posso usar o primeiro no meu código.

var orders = Customers.SelectMany(c => c.Orders)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top