Как компилаторът на C# избира SelectMany при превод на LINQ израз?

Има 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
)

Въпросът ми е: как C# компилаторът избира SelectMany, когато превежда LINQ израз в извикване на метод на разширение?

По принцип, ако има няколко от в LINQ израза, ще има SelectMany. Но изглежда, че C# компилаторът избира само втория подпис. Първият подпис никога не се използва.

        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?

Благодаря.


person Morgan Cheng    schedule 08.01.2009    source източник


Отговори (2)


Според C# Spec, компилаторът няма да генерира извикване за претоварване към първата версия на SelectMany. Първата версия на SelectMany е полезна за изравняване на списък със списъци в единичен плосък списък.

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

Той няма силен еквивалент в израз на заявка.

Вижте раздел 7.15.2 от спецификацията на езика C# за повече информация.

person JaredPar    schedule 08.01.2009
comment
Наистина... void функция връща нещо и никой не я коментира? - person Marcel Popescu; 20.07.2010

защо имаме 2 подписа на SelectMany?

Така че мога да използвам първия в моя код.

var orders = Customers.SelectMany(c => c.Orders)
person Amy B    schedule 09.01.2009
comment
Със сигурност. Това е само за удобство. - person Alex Yakunin; 18.11.2009
comment

То крещи, защото не можете да направите това.

Като алтернатива предлагам следното:

#ifdef DEBUG
#define DO(WHAT) MyObj->WHAT()
#else
#define DO(WHAT)
#endif
- person Reb.Cabin; 03.06.2011