Синтаксис запроса или синтаксис метода (LINQ)
Обновлен: Ноябрь 2007
Большинство запросов в вводной документации по LINQ написаны как выражения запросов с помощью декларативного синтаксиса запроса, представленного в C# 3.0. Однако в самой общеязыковой среде выполнения (CLR) .NET отсутствует понятие синтаксиса запроса. Таким образом, во время компиляции выражения запроса преобразуются в то, что понятно CLR — вызовы методов. Эти методы называются стандартными операторами запросов и они имеют такие имена, как Where, Select, GroupBy, Join, Max, Average и т. д. Их можно вызывать непосредственно, используя синтаксис методов вместо синтаксиса запросов.
В целом, рекомендуется синтаксис запросов, так как обычно он более прост и легко читается; однако между синтаксисом методов и синтаксисом запросов нет семантической разницы. Кроме того, некоторые запросы, например такие, которые извлекают количество элементов, соответствующих указанному условию, или которые извлекают элемент, имеющий максимальное значение в исходной последовательности, могут быть выражены только в виде вызовов методов. В справочной документации по стандартным операторам запросов в пространстве имен System.Linq обычно используется синтаксис методов. Поэтому, даже на начальном этапе написания запросов LINQ полезно знать, как использовать синтаксис методов в запросах и самих выражениях запроса.
Методы расширения стандартных операторов запросов
В следующем примере показано простое выражение запроса и семантически эквивалентный ему запрос, написанный как запрос на основе метода.
class QueryVMethodSyntax
{
static void Main()
{
int[] numbers = { 5, 10, 8, 3, 6, 12};
//Query syntax:
IEnumerable<int> numQuery1 =
from num in numbers
where num % 2 == 0
orderby num
select num;
//Method syntax:
IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);
foreach (int i in numQuery1)
{
Console.Write(i + " ");
}
Console.WriteLine(System.Environment.NewLine);
foreach (int i in numQuery2)
{
Console.Write(i + " ");
}
// Keep the console open in debug mode.
Console.WriteLine(System.Environment.NewLine);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
/*
Output:
6 8 10 12
6 8 10 12
*/
Два примера имеют идентичные результаты. Тип переменной запроса одинаковый в обеих формах: IEnumerable<T>.
Чтобы понять запрос на основе метода, рассмотрим его более детально. Обратите внимание, что в правой части выражения предложение where теперь выражено в виде метода экземпляра объекта numbers, который имеет тип IEnumerable<int>. Если вы знакомы с универсальным интерфейсом IEnumerable<T>, вам известно, что он не имеет метода Where. Однако при вызове списка завершения IntelliSense в IDE Visual Studio будет отображен не только метод Where, но и многие другие методы, такие как Select, SelectMany, Join и Orderby. Они все являются стандартными операторами запросов.
Несмотря на то, что кажется, как будто интерфейс IEnumerable<T> был переопределен для включения этих дополнительных методов, на самом деле это не так. Стандартные операторы запросов реализуются как новый тип методов, называемых методами расширения. Методы расширения "расширяют" существующий тип; их можно вызывать так, как если бы они были методами экземпляра типа. Стандартные операторы запросов расширяют IEnumerable<T>, что позволяет написать numbers.Where(...).
Все, что нужно знать о методах расширения, чтобы начать работу с LINQ, — способы их добавления в область видимости в приложении с помощью подходящих директив using. Они объясняются дополнительно в разделе Практическое руководство. Создание проекта LINQ. С точки зрения приложения методы расширения и обычные методы экземпляра одинаковы.
Дополнительные сведения о методах расширения см. в разделе Методы расширения (руководство по программированию в C#). Дополнительные сведения о стандартных операторах запросов см. в разделах Общее руководство программирования на LINQ и Общие сведения о стандартных операторах запроса. Некоторые поставщики LINQ, например LINQ to SQL и LINQ to XML, реализуют свои собственные стандартные операторы запросов и дополнительные методы расширения для типов, отличных от IEnumerable<T>.
Лямбда-выражения
Обратите внимание, что в предыдущем примере условное выражение (num % 2 == 0) передается в качестве встроенного аргумента методу Where: Where(num => num % 2 == 0). Это встроенное выражение называется лямбда-выражением. Оно является удобным способом написания кода, который в противном случае пришлось бы записывать в более громоздкой форме как анонимный метод, универсальный делегат или дерево выражений. В C# => является лямбда-оператором, который читается как "переходит". num слева от оператора является входной переменной, которая соответствует num в выражении запроса. Компилятор может определить тип num, так как ему известно, что numbers является универсальным типом IEnumerable<T>. Основная часть лямбда-выражения представляет то же самое, что и выражение в синтаксисе запроса или в любом другом выражении или операторе C#; она может включать вызовы методов и другую сложную логику. Возвращаемым значением является просто результат выражения.
Приступая к работе с LINQ, нет необходимости широко использовать лямбда-выражения. Однако некоторые запросы могут выражаться только в синтаксисе методов, а некоторые из них требуют лямбда-выражений. После знакомства с лямбда-выражениями станет понятно, что они являются мощными и гибкими элементами в панели элементов LINQ. Дополнительные сведения см. в разделе Лямбда-выражения (Руководство по программированию в C#).
Возможность компоновки запросов
Обратите внимание, что в предыдущем примере метод OrderBy вызывается применением оператора dot к вызову Where. Where создает отфильтрованную последовательность, а затем Orderby работает с ней, сортируя ее. Поскольку запросы возвращают IEnumerable, их можно компоновать в синтаксисе методов, объединяя вызовы методов в цепочки. При использовании синтаксиса запросов эти действия выполняет компилятор. Поскольку переменная запроса не сохраняет результаты запроса, ее можно изменить или в любое время использовать в качестве основы для нового запроса, даже после ее выполнения.