Изучая LINQ наткнулся на то, что результат запроса имеет такой тип, но информации по нему найти не удалось.
Информации не удалось найти из-за закона Деметры.
объект должен иметь как можно меньше представления о структуре и свойствах чего угодно
Класс WhereSelectArrayIterator реализует Декоратор и Адаптер Итератора. Декоратор, Адаптер и Итератор — три паттерна, описанные в классической книге GoF.
Метод Enumerable.Select получает на вход Итератор (любой класс, реализующий интерфейс IEnumerable<T>) и возвращает другой Итератор. Новый Итератор является Адаптером потому что преобразует интерфейс IEnumerable<T1> в интерфейс IEnumerable<T2>.
Для того, чтобы вызывать метод Select нам достаточно знать только, что на входе и выходе у нас IEnumerable<T>. По закону Деметры знать реальный тип возвращаемого объекта нам не нужно.
Если мы не знаем деталей реализации, то создатели LINQ вольны выбирать любую реализацию, и наш код продолжит работать корректно.
Как видим, создатели LINQ, очевидно, в целях минимизации кода, сделали один класс для выполнения двух разных операций: Where и Select. Так что здесь реализован не только паттерн Адаптер, но и паттерн Декоратор, который добавляет новое поведение, а именно фильтрацию. В целях оптимизации они сделали разные классы для коллекций тех типов, с которыми они умеют работать быстро в обход стандартного интерфейса, а именно, для списков и для массивов.
Таким образом, получились классы WhereSelectArrayIterator<T1, T2> и WhereSelectListIterator<T1, T2>. Каждый из них, в зависимости от параметров конструктора, выполняет фильтрацию и отображение входной последовательности IEnumerable<T1> в выходную последовательность IEnumerable<T2>. Первый предназначен для быстрой обработки массивов, а второй — списков. Это то, что мы можем предположить. При прочих равных условиях большего знать нам и не нужно.
Этот тип возвращается функцией Select при следующих вызовах если она применена к массиву:
arr.Select(...)
arr.Select(...).Select(...)
arr.Where(...).Select(...)
arr.Where(...).Select(...).Select(...)
arr.Where(...).Where(...).Select(...)
И так далее.
Вот прямо так и проверяют:
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
// ...
if (source is TSource[])
{
return new Enumerable.WhereSelectArrayIterator<TSource, TResult>((TSource[])source, null, selector);
}
// ...
}
Смысл существования данного класса - оптимизация LINQ при работе с массивами. Данный класс реализует интерфейсы IEnumerable и IEnumerator.
Исходный код этого класса можно увидеть тут: https://github.com/Microsoft/referencesource/blob/master/System.Core/System/Linq/Enumerable.cs#L418
Реализация IEnumerator<TResult> у этого класса тривиальная - просто хранится индекс текущего элемента в поле index, в методе MoveNext он увеличивается пока не дойдет до допустимого элемента или до конца массива.
Реализация IEnumerable<TResult> у этого класса сложнее и спрятана в базовом классе Iterator<TResult>. Там используется популярный трюк для уменьшения нагрузки на сборщик мусора: в методе GetEnumerator() делается проверка что объект создан в текущем потоке и что метод GetEnumerator() еще ни разу не вызывался; если это так то возвращается this, иначе возвращается новый объект.
Наконец, довольно важной частью являются методы Select и Where, которые используются для оптимизации построения цепочек из методов LINQ.
Метод Select возвращает новый WhereSelectArrayIterator, тем самым позволяя тянуть цепочку из вызовов .Select() сохраняя оптимальный доступ к массиву.
Метод Where не дает новых оптимизаций, но нужен потому что объявлен абстрактным в базовом классе. Он возвращает WhereEnumerableIterator который работает с произвольной последовательностью.
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости