Comment le compilateur C # a-t-il choisi SelectMany lors de la traduction d'une expression LINQ?
-
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.
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)