Comment le compilateur C # a-t-il choisi SelectMany lors de la traduction d'une expression LINQ?

StackOverflow https://stackoverflow.com/questions/422958

  •  05-07-2019
  •  | 
  •  

Question

Il y a 4 signatures surchargées pour Enumerable.SelectMany. Pour simplifier, nous ignorons les deux signatures avec l’argument int . Nous avons donc 2 signatures pour 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
)

Ma question est la suivante: comment le compilateur C # a-t-il choisi SelectMany lors de la traduction d'une expression LINQ en invocation d'une méthode d'extension?

En gros, s’il existe plusieurs sources dans l’expression LINQ, il y aura SelectMany. Mais, il semble que le compilateur C # ne choisisse que la deuxième signature. La première signature n'est jamais utilisée.

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

Avec l'aide de Reflector, je peux voir que l'expression LINQ ci-dessus est traduite en

en1.SelectMany<int, double, string>(delegate (int i1) {
        return en2;
    }, delegate (int i1, double i2) {
        double CS
        IEnumerable<int> en4 =
            from i1 in en1
            from i2 in Enumerable.Range(0, i1)
            select i2;
en1.SelectMany<int, int, int>(delegate (int i1) {
        return Enumerable.Range(0, i1);
    }, delegate (int i1, int i2) {
        return i2;
    })
= i1 * i2return CS<*><*>.ToString(); })

L'exemple ci-dessus implique 3 types. Il est donc raisonnable de sélectionner la deuxième signature SelectMany. Cependant, pour l'exemple ci-dessous, un seul type est impliqué, il sélectionne toujours la deuxième signature.

<*>

Cela se traduit par:

<*>

Par conséquent, je ne trouve pas le cas où l'expression LINQ est traduite dans la première signature SelectMany. Y at-il un tel cas?

Si la première signature SelectMany n’est pas utilisée, elle existe simplement parce qu’elle est BIND of monad en programmation fonctionnelle?

Peut-être que la question peut être: pourquoi avons-nous deux signatures de SelectMany?

Merci.

Était-ce utile?

La solution

Selon la spécification C #, le compilateur ne générera pas d'appel de surcharge à la première version de SelectMany. La première version de SelectMany est utile pour aplatir une liste de listes en une seule liste à plat.

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

Il n’a pas d’équivalent fort dans une expression de requête.

Voir la section 7.15.2 de la spécification du langage C # pour plus d'informations.

Autres conseils

  

pourquoi avons-nous 2 signatures de SelectMany?

Je peux donc utiliser le premier dans mon code.

var orders = Customers.SelectMany(c => c.Orders)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top