Модифицируйте код метода
QueryOverStrings()
следующим образом:
// Построить выражение запроса для поиска
// в массиве элементов, содержащих пробел.
IEnumerable<string> subset =
from g in currentVideoGames
where g.Contains(" ")
orderby g
select g;
<b>ReflectOverQueryResults(subset);</b>
// Вывести результаты.
foreach (string s in subset)
{
Console.WriteLine("Item: {0}", s);
}
Запустив приложение, легко заметить, что переменная
subset
в действительности представляет собой экземпляр обобщенного типа
OrderedEnumerable<TElement, ТКеу>
(представленного в коде CIL как
OrderedEnumerable`2
), который является внутренним абстрактным типом, находящимся в сборке
System.Linq.dll
:
***** Info about your query using Query Expressions*****
resultSet is of type: OrderedEnumerable`2
resultSet location: System.Linq
Внесите такое же изменение в код метода
QueryOverStringsWithExtensionMethods()
, но передав во втором параметре строку "
Extension Methods
":
// Построить выражение запроса для поиска
// в массиве элементов, содержащих пробел.
IEnumerable<string> subset =
currentVideoGames
.Where(g => g.Contains(" "))
.OrderBy(g => g)
.Select(g => g);
<b>ReflectOverQueryResults(subset,"Extension Methods");</b>
// Вывести результаты.
foreach (string s in subset)
{
Console.WriteLine("Item: {0}", s);
}
После запуска приложения выяснится, что переменная
subset
является экземпляром типа
SelectIPartitionIterator
. Но если удалить из запроса конструкцию
Select(g=>g)
, то
subset
снова станет экземпляром типа
OrderedEnumerable<TElement, ТКеу>
. Что все это значит? Для подавляющего большинства разработчиков немногое (если вообще что-либо). Оба типа являются производными от
IEnumerable<T>
, проход по ним осуществляется одинаковым образом и они оба способны создавать список или массив своих значений.
***** Info about your query using Extension Methods *****
resultSet is of type: SelectIPartitionIterator`2
resultSet location: System.Linq
LINQ и неявно типизированные локальные переменные
Хотя в приведенной программе относительно легко выяснить, что результирующий набор может быть интерпретирован как перечисление объектов
string
(например,
IEnumerable<string>
), тот факт, что подмножество на самом деле имеет тип
OrderedEnumerable<TElement, ТКеу>
, не настолько ясен.
Поскольку результирующие наборы LINQ могут быть представлены с применением порядочного количества типов из разнообразных пространств имен LINQ, было бы утомительно определять подходящий тип для хранения результирующего набора. Причина в том, что во многих случаях лежащий в основе тип не очевиден и даже напрямую не доступен в коде (и как вы увидите, в ряде ситуаций тип генерируется на этапе компиляции).
Чтобы еще больше подчеркнуть данное обстоятельство, ниже показан дополнительный вспомогательный метод, определенный внутри класса
Program
:
static void QueryOverInts()
{
int[] numbers = {10, 20, 30, 40, 1, 2, 3, 8};
// Вывести только элементы меньше 10.
IEnumerable<int> subset = from i in numbers where i < 10 select i;
foreach (int i in subset)
{
Console.WriteLine("Item: {0}", i);
}
ReflectOverQueryResults(subset);
}
В рассматриваемом случае переменная
subset
имеет совершенно другой внутренний тип. На этот раз тип, реализующий интерфейс
IEnumerable<int>
, представляет собой низкоуровневый класс по имени
WhereArrayIterator<T>
:
Item: 1
Item: 2
Item: 3
Item: 8
***** Info about your query *****
resultSet is of type: WhereArrayIterator`1
resultSet location: System.Linq
Учитывая, что точный тип запроса LINQ не вполне очевиден, в первых примерах результаты запросов были представлены как переменная
IEnumerable<T>
, где
Т
— тип данных в возвращенной последовательности (
string
,
int
и т.д.). Тем не менее, ситуация по-прежнему довольно запутана. Чтобы еще больше все усложнить, стоит упомянуть, что поскольку интерфейс
IEnumerable<T>
расширяет необобщенный
IEnumerable
, получать результат запроса LINQ допускается и так: