In che modo il compilatore C # sceglie SelectMany durante la traduzione dell'espressione LINQ?
-
05-07-2019 - |
Domanda
Esistono 4 firme sovraccaricate per Enumerable.SelectMany. Per semplificare, ignoriamo le due firme con l'argomento int . Quindi abbiamo 2 firme per 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
)
La mia domanda è: in che modo il compilatore C # sceglie SelectMany quando traduce l'espressione LINQ in invocazione del metodo di estensione?
Fondamentalmente, se ci sono più da nell'espressione LINQ, ci sarà SelectMany. Ma sembra che il compilatore C # scelga solo la seconda firma. La prima firma non viene mai utilizzata.
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 l'aiuto di Reflector, posso vedere che l'espressione LINQ sopra è tradotta in
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'esempio sopra riguarda 3 tipi. Pertanto, è ragionevole selezionare la seconda firma SelectMany. Tuttavia, per esempio di seguito, è coinvolto solo un tipo, seleziona comunque la seconda firma.
<*>È tradotto in:
<*>Quindi, non riesco a trovare un caso in cui l'espressione LINQ sia tradotta nella prima firma SelectMany. Esiste un caso del genere?
Se la prima firma SelectMany non viene utilizzata, esiste solo perché è BIND di monade nella programmazione funzionale?
Forse la domanda può essere: perché abbiamo 2 firme di SelectMany?
Grazie.
Soluzione
Secondo le specifiche C #, il compilatore non genererà una chiamata di sovraccarico alla prima versione di SelectMany. La prima versione di SelectMany è utile per appiattire un elenco di elenchi in un unico elenco piatto.
public IEnumerable<string> Example(IEnumerable<IEnumerable<string>> enumerable) {
return enumerable.SelectMany(x => x);
}
Non ha un equivalente forte in un'espressione di query.
Per ulteriori informazioni, consultare la Sezione 7.15.2 delle specifiche del linguaggio C #.
Altri suggerimenti
perché abbiamo 2 firme di SelectMany?
Quindi posso usare il primo nel mio codice.
var orders = Customers.SelectMany(c => c.Orders)