Как реализовать методы LINQ с помощью SelectMany?

Эрик Мейер увлекается тем, что каждая функция LINQ может быть реализована SelectMany; все остальное - просто удобство.

Вот что Эрик Липперт отвечает на вопрос о монадах, но я слышал, как Эрик Мейер сказал это в других видео о LINQ и Rx. (Проще говоря, Эрик Мейер - парень, который создал LINQ и Rx)

Интересно, как реализовать некоторые из наиболее часто используемых функций LINQ с помощью SelectMany? Игнорируйте perfromance на данный момент, позвольте сосредоточиться на элегантности и лаконичности.

  • Где
  • Выберите
  • Первая
  • Возьмите (п)
  • TakeWhile
  • GroupBy
  • OrderBy
  • Zip
  • Другие...

Ответ 1

Основной бит, о котором следует помнить, заключается в том, что SelectMany работает с IEnumerable и возвращает IEnumerable, используя лямбда-выражения, имеющие доступ к текущему элементу и его индексу. Итак, все, что вы могли бы сделать, чтобы преобразовать результат с доступом к текущему элементу или его индексу, возможно:

  • Определите, какие элементы сохранить или выбросить (Where, First, Take, Skip, TakeWhile)
  • Измените элементы и верните новые (Select)
  • Есть ли другие вещи? (GroupBy, честно говоря, я не знаю, как бы это реализовать, не задумываясь)

Этот простой пример Where даст понять, как многие из них могут быть выполнены легко:

SomeList.SelectMany(x =>
    ShouldBeIncluded(x) ?
        Enumerable.Repeat(x, 1) :
        Enumerable.Empty<AClass>();
    );

Edit - Great размещенная Тимом Шмельтером в комментариях, снова подтверждает, что Джон Скит уже сделал это более чистым:

return Enumerable.Repeat(x, ShouldBeIncluded(x) ? 1 : 0;

Ответ 2

Ну, это вернет Enumerable<T> с нулевым или одним элементом, лучше ничего:

public static IEnumerable<T> FirstOrDefault<T>(this IEnumerable<T> source)
{
    return source.SelectMany((t, index) => Enumerable.Repeat(t, index == 0 ? 1 : 0));
}

Не вижу, как вернуть только T с помощью SelectMany.