Синтаксис запроса или синтаксис метода (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. Они все являются стандартными операторами запросов.

Стандартный оператор запроса в Intellisense

Несмотря на то, что кажется, как будто интерфейс 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, их можно компоновать в синтаксисе методов, объединяя вызовы методов в цепочки. При использовании синтаксиса запросов эти действия выполняет компилятор. Поскольку переменная запроса не сохраняет результаты запроса, ее можно изменить или в любое время использовать в качестве основы для нового запроса, даже после ее выполнения.

См. также

Другие ресурсы

Приступая к работе с LINQ в C#