Перечислитель и возвращаемый тип LINQ

Рейтинг: 1Ответов: 2Опубликовано: 02.02.2015

Господа, есть два кусочка кода:

int[] nums = { 1, -2, 3, 0, -4, 5 };

и

var posNums = from n in nums
                  where n > 0
                  select n;

Собственно, вопрос у меня такой: я не могу понять, какой тип данных получается у переменной posNums. Вначале я решил, что возвращается коллекция, но понял, что это не так. Выходит, что возвращается перечислитель. Однако, насколько я понимаю, то перечислитель занимается "перечислением" набора данных в определенном классе, который содержит эти данные, то есть коллекции или массиве. Тогда в связи с этим возникает вопрос, данный LINQ запрос возвращает определенный класс или что он вообще возвращает? При использовании GetType() я получаю, wherearrayiterator, но что это значит, я не пойму, и гугл тоже. Просветите, пожалуйста. Спасибо.

Ответы

▲ 6Принят

Вы забиваете себе голову какими-то совершенно ненужными деталями. При работе с Linq важно понять - все эти страшно называющиеся типы данных вам абсолютно не нужны. Вам нужен только один тип данных (точнее интерфейс) - это IEnumerable<T>. Ничего другого (с некоторыми оговорками, в частности IQueriable) о типах в Linq знать не нужно. При рабте с linq вы должны знать, что такое IEnumerable, а именно что это некая последовательность, о которой мы лишь знаем, из элементов какого типа она состоит. В том числе мы не знаем ничего о ее длине (это намек на то, что непосредственно у IEnumerable нет метода Count, и вызов его влечет за собой перечисление коллекции), мы не можем в нее добавлять или удалять из нее элементы, но мы знаем очень важную вещь - все операции в linq оперируют с этим интерфейсом. (также стоит отметить, что у этого интерфейса есть две важных особенности - опасность многократного перечисления и "ленивые" вычисления, о которых нужно говорить отдельно)

Никогда программист, работая с linq, не пишет что-то типа

WhereArrayIterator foo = from ...

Он напишет

IEnumerable<Foo> foo = from ...

а еще лучше

var foo = from ...

поскольку тип данных тут имеет весьма посредственную роль (а иногда его и вовсе невозможно вывести в compile-time)

▲ 7

Какой именно тип возвращается, не так уж важно, тем более, что никакой конкретный тип не гарантирован. Хуже того, этот тип имеет право меняться при изменении типа коллекции nums. И конечно может меняться со сменой настроек оптимизации, выходом следующей версии компилятора или инсталляцией сервис-пака на машине клиента.

Единственное, что гарантировано — это что возвращаемый тип поддерживает интерфейс IEnumerable<T>. Также, если я не ошибаюсь, гарантировано, что доступа к nums не будет до момента фактического перечисления (то, что называется lazy evaluation), поэтому такой код:

int nums[] = { 1, -2, 3, 0, -4, 5 };
var posNums = from n in nums
              where n > 0
              select n;
nums[0] = -1;
Console.WriteLine(posNums.First());

выведет 3, а не 1.


wherearrayiterator — это внутренняя подробность текущей имплементации. Не стоит полагаться на неё.