Когда требуется оценить выражение LINQ, выдающее последовательность, за пределами логики
foreach
, можно вызывать любое количество расширяющих методов, определенных в типе
Enumerable
, таких как
ТоArray<Т>
,
ToDictionary<TSource, ТКеу>
и
ToList<T>
. Все методы приводят к выполнению запроса LINQ в момент их вызова для получения снимка данных. Затем полученным снимком данных можно
манипулировать независимым образом.
Кроме того, запрос выполняется немедленно в случае поиска только одного элемента. Метод
First
возвращает первый элемент последовательности (и должен всегда применяться с конструкцией
orderby
). Метод
FirstOrDefault
возвращает стандартное значение для типа элемента в последовательности, если возвращать нечего, например, когда исходная последовательность пуста или конструкция
where
отбросила все элементы. Метод
Single
также возвращает первый элемент последовательности (на основе
orderby
или согласно порядку следования элементов, если конструкция
orderby
отсутствует). Подобно аналогично именованному эквиваленту метод
SingleOrDefault
возвращает стандартное значение для типа элемента в последовательности, если последовательность пуста (или конструкция
where
отбросила все элементы). Отличие между методами
First(OrDefault)
и
Single(OrDefault)
заключается в том, что
Single(OrDefault)
сгенерирует исключение, если из запроса будет возвращено более одного элемента.
static void ImmediateExecution
{
Console.WriteLine;
Console.WriteLine("Immediate Execution");
int[] numbers = { 10, 20, 30, 40, 1, 2, 3, 8 };
// Получить первый элемент в порядке последовательности
int number = (from i in numbers select i).First;
Console.WriteLine("First is {0}", number);
// Получить первый элемент в порядке запроса
number = (from i in numbers orderby i select i).First;
Console.WriteLine("First is {0}", number);
// Получить один элемент, который соответствует запросу
number = (from i in numbers where i > 30 select i).Single;
Console.WriteLine("Single is {0}", number);
try
{
// В случае возвращения более одного элемента генерируется исключение
number = (from i in numbers where i > 10 select i).Single;
(from i in numbers where i < 10 select i).ToArray<int>;
// Получить данные НЕМЕДЛЕННО как List<int>.
List<int> subsetAsListOfInts =
(from i in numbers where i < 10 select i).ToList<int>;
}
Обратите
внимание, что для вызова методов
Enumerable
выражение LINQ целиком помещено в круглые скобки с целью приведения к корректному внутреннему типу (каким бы он ни был).
Вспомните из главы 10, что если компилятор C# в состоянии однозначно определить параметр типа обобщенного элемента, то вы не обязаны указывать этот параметр типа. Следовательно,
ТоArray<Т>
(или
ToList<T>
) можно было бы вызвать так:
int[] subsetAsIntArray =
(from i in numbers where i < 10 select i).ToArray;
Полезность немедленного выполнения очевидна, когда нужно возвратить результаты запроса LINQ внешнему вызывающему коду, что и будет темой следующего раздела главы.
Возвращение результатов запроса LINQ
Внутри класса (или структуры) можно определить поле, значением которого будет результат запроса LINQ. Однако для этого нельзя использовать неявную типизацию (т.к. ключевое слово
var
не может применяться к полям), и целью запроса LINQ не могут быть данные уровня экземпляра, а потому он должен быть статическим. С учетом указанных ограничений необходимость в написании кода следующего вида будет возникать редко:
class LINQBasedFieldsAreClunky
{
private static string[] currentVideoGames =
{"Morrowind", "Uncharted 2",
"Fallout 3", "Daxter", "System Shock 2"};
// Здесь нельзя использовать неявную типизацию!
// Тип subset должен быть известен!
private IEnumerable<string> subset =
from g in currentVideoGames
where g.Contains(" ")
orderby g
select g;
public void PrintGames
{
foreach (var item in subset)
{
Console.WriteLine(item);
}
}
}
Запросы LINQ часто определяются внутри области действия метода или свойства. Кроме того, для упрощения программирования результирующий набор будет храниться в неявно типизированной локальной переменной, использующей ключевое слово
var
. Вспомните из главы 3, что неявно типизированные переменные не могут применяться для определения параметров, возвращаемых значений, а также полей класса или структуры.
Итак, вполне вероятно, вас интересует, каким образом возвратить результат запроса внешнему коду. Ответ: в зависимости от обстоятельств. Если у вас есть результирующий набор, состоящий из строго типизированных данных, такой как массив строк или список
List<T>
объектов
Car
, тогда вы могли бы отказаться от использования ключевого слова