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

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

  •  05-07-2019
  •  | 
  •  

Вопрос

Существует 4 перегруженных подписи для Enumerable.Выберите Many.Чтобы упростить задачу, мы игнорируем две подписи с инт аргумент.Итак, у нас есть 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 не используется, значит, она существует только потому, что она является ПРИВЯЗКОЙ монады в функциональном программировании?

Возможно, вопрос может быть таким:почему у нас есть 2 подписи SelectMany?

Спасибо.

Это было полезно?

Решение

Согласно спецификации C #, компилятор не будет генерировать вызов перегрузки для первой версии SelectMany.Первая версия SelectMany полезна для сведения списка списков в единый плоский список.

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

У него нет строгого эквивалента в выражении запроса.

Смотрите Раздел 7.15.2 спецификации языка C # для получения дополнительной информации.

Другие советы

почему у нас есть 2 подписи SelectMany?

Таким образом, я могу использовать первый вариант в своем коде.

var orders = Customers.SelectMany(c => c.Orders)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top