Что представляет собой тип System.Linq.Enumerable.WhereSelectArrayIterator<T1,T2>?

231
21 февраля 2018, 07:29

Изучая LINQ наткнулся на то, что результат запроса имеет такой тип, но информации по нему найти не удалось.

Answer 1

Информации не удалось найти из-за закона Деметры.

объект должен иметь как можно меньше представления о структуре и свойствах чего угодно

Класс 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>. Первый предназначен для быстрой обработки массивов, а второй — списков. Это то, что мы можем предположить. При прочих равных условиях большего знать нам и не нужно.

Answer 2

Этот тип возвращается функцией 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 который работает с произвольной последовательностью.

READ ALSO
Инъекция DetailRow внутрь ItemTemplate

Инъекция DetailRow внутрь ItemTemplate

Имеется табличка:

157
Помогите с заменой и печатью C#

Помогите с заменой и печатью C#

У меня есть документ: Поставкаdocx

171
получение данных из 1с в с#

получение данных из 1с в с#

Нужно написать программу чтобы получить данные(таблицы) из 1С в с#примерно так же как брать данные с базы данных access

158
Создание в Unity partial классов в проекте Editor

Создание в Unity partial классов в проекте Editor

В официальной документации сказано что юнити понимает partial классы шарпаВот мой пример:

196