Синтаксис запросов и синтаксис методов в LINQ (C#)

Большинство запросов в вводной документации LINQ (LINQ) записываются с помощью декларативного синтаксиса запросов LINQ.Однако синтаксис запроса должны быть преобразованы в вызовы методов для среды 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 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 вызывается с помощью оператора точки при вызове Where.Выражение Where создает фильтрованную последовательность, и метод Orderby работает с этой последовательностью, сортируя ее.Поскольку запросы возвращают IEnumerable, их можно компоновать в синтаксисе методов, объединяя вызовы методов в цепочки.При использовании синтаксиса запросов эти действия выполняет компилятор.Поскольку переменная запроса не сохраняет результаты запроса, ее можно изменить или в любое время использовать в качестве основы для нового запроса, даже после ее выполнения.

См. также

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

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