Учитывая, что точный тип запроса LINQ не вполне очевиден, в первых примерах результаты запросов были представлены как переменная
IEnumerable<T>
, где
Т
— тип данных в возвращенной последовательности (
string
,
int
и т.д.). Тем не менее, ситуация по-прежнему довольно запутана. Чтобы еще больше все усложнить, стоит упомянуть, что поскольку интерфейс
IEnumerable<T>
расширяет необобщенный
IEnumerable
,
получать результат запроса LINQ допускается и так:
System.Collections.IEnumerable subset =
from i in numbers
where i < 10
select i;
К счастью, неявная типизация при работе с запросами LINQ значительно проясняет картину:
static void QueryOverInts
{
int[] numbers = {10, 20, 30, 40, 1, 2, 3, 8};
// Здесь используется неявная типизация...
var subset = from i in numbers where i < 10 select i;
// ...и здесь тоже.
foreach (var i in subset)
{
Console.WriteLine("Item: {0} ", i);
}
ReflectOverQueryResults(subset);
}
В качестве эмпирического правила: при захвате результатов запроса LINQ всегда необходимо использовать неявную типизацию. Однако помните, что (в большинстве случаев) действительное возвращаемое значение имеет тип, реализующий интерфейс
IEnumerable<T>
.
Какой точно тип кроется за ним (
OrderedEnumerable<TElement, ТКеу>
,
WhereArrayIterator<T>
и т.п.), к делу не относится, и определять его вовсе не обязательно. Как было показано в предыдущем примере кода, для прохода по извлеченным данным можно просто применить ключевое слово var внутри конструкции
foreach
.
LINQ и расширяющие методы
Несмотря на то что в текущем примере совершенно не требуется напрямую писать какие-то расширяющие методы, на самом деле они благополучно используются на заднем плане. Выражения запросов LINQ могут применяться для прохода по содержимому контейнеров данных, которые реализуют обобщенный интерфейс
IEnumerable<T>
. Тем не менее, класс
System.Array
(используемый для представления массива строк и массива целых чисел) не реализует этот контракт:
// Похоже, что тип System.Array не реализует
// корректную инфраструктуру для выражений запросов!
public abstract class Array : ICloneable, IList,
IStructuralComparable, IStructuralEquatable
{
...
}
Хотя класс
System.Array
не реализует напрямую интерфейс
IEnumerable<T>
, он косвенно получает необходимую функциональность данного типа (а также многие другие члены, связанные с LINQ) через статический тип класса
System.Linq.Enumerable
.
В служебном классе
System.Linq.Enumerable
определено множество обобщенных расширяющих методов (таких как
Aggregate<T>
,
First<T>
,
Мах<Т>
и т.д.),
которые класс
System.Array
(и другие типы) получают в свое распоряжение на заднем плане. Таким образом, если вы примените операцию точки к локальной переменной
currentVideoGames
, то обнаружите большое количество членов, которые отсутствуют в формальном определении
System.Array
.
Роль отложенного выполнения
Еще один важный момент, касающийся выражений запросов LINQ, заключается в том, что фактически они не оцениваются до тех пор, пока не начнется итерация по результирующей последовательности. Формально это называется отложенным выполнением. Преимущество такого подхода связано с возможностью применения одного и того же запроса LINQ многократно к тому же самому контейнеру и полной гарантией получения актуальных результатов. Взгляните на следующее обновление метода
QueryOverlnts
:
static void QueryOverInts
{
int[] numbers = { 10, 20, 30, 40, 1, 2, 3, 8 };
// Получить числа меньше 10.
var subset = from i in numbers where i < 10 select i;
// Оператор LINQ здесь оценивается!
foreach (var i in subset)
{
Console.WriteLine("{0} < 10", i);
}
Console.WriteLine;
// Изменить некоторые данные в массиве.
numbers[0] = 4;
// Снова производится оценка!
foreach (var j in subset)
{
Console.WriteLine("{0} < 10", j);
}
Console.WriteLine;
ReflectOverQueryResults(subset);
}
На заметку! Когда оператор LINQ выбирает одиночный элемент (с использованием
First/FirstOrDefault
,
Single/SingleOrDefault
или любого метода агрегирования), запрос выполняется немедленно. Методы
First
,
FirstOrDefault
,
Single
и
SingleOrDefault
будут описаны в следующем разделе. Методы агрегирования раскрываются позже в главе.
Ниже показан вывод, полученный в результате запуска программы. Обратите внимание, что во второй итерации по запрошенной последовательности появился дополнительный член, т.к. для первого элемента массива было установлено значение меньше 10:
1 < 10
2 < 10
3 < 10
8 < 10
4 < 10
1 < 10
2 < 10
3 < 10
8 < 10
Среда Visual Studio обладает одной полезной особенностью: если вы поместите точку останова перед оценкой запроса LINQ, то получите возможность просматривать содержимое во время сеанса отладки. Просто наведите курсор мыши на переменную результирующего набора LINQ (
subset
на рис. 13.1) и вам будет предложено выполнить запрос, развернув узел Results View (Представление результатов).