Существует 4 перегруженных подписи для Enumerable.SelectMany. Для простоты мы игнорируем две подписи с аргументом int. Итак, у нас есть 2 подписи для 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
)
Мой вопрос: как компилятор С# выбирает SelectMany при переводе выражения LINQ в вызов метода расширения?
По сути, если в выражении LINQ есть несколько from, будет SelectMany. Но, похоже, компилятор С# выбирает только вторую подпись. Первая подпись никогда не используется.
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);
}
С помощью Reflector я вижу, что приведенное выше выражение LINQ переводится в
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();
})
В приведенном выше примере используются 3 типа. Итак, разумно выбрать вторую сигнатуру SelectMany. Однако в приведенном ниже примере задействован только один тип, он по-прежнему выбирает вторую подпись.
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;
})
Итак, я не могу найти случай, когда выражение LINQ транслируется в первую подпись SelectMany. Есть такой случай?
Если первая сигнатура SelectMany не используется, то она существует только потому, что это BIND монады в функциональном программировании?
Наверно вопрос может быть: почему у нас 2 подписи SelectMany?
Спасибо.