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

Большинство запросов в вводной документации к LINQ (LINQLINQ) написано с использованием декларативного синтаксиса запросов LINQ.Most queries in the introductory Language Integrated Query (LINQLINQ) documentation are written by using the LINQ declarative query syntax. Однако синтаксис запроса должен быть преобразован в вызовы методов для среды CLR .NET, когда код компилируется.However, the query syntax must be translated into method calls for the .NET common language runtime (CLR) when the code is compiled. Эти вызовы метода вызывают стандартные операторы запросов, которые имеют такие имена, как Where, Select, GroupBy, Join, Max и Average.These method calls invoke the standard query operators, which have names such as Where, Select, GroupBy, Join, Max, and Average. Вместо синтаксиса запросов для их вызова можно использовать синтаксис методов.You can call them directly by using method syntax instead of query syntax.

Синтаксис запросов и синтаксис методов семантически идентичны, но многие пользователи найдут синтаксис запросов более простым и более удобным для чтения.Query syntax and method syntax are semantically identical, but many people find query syntax simpler and easier to read. Некоторые запросы должны быть выражены как вызовы методов.Some queries must be expressed as method calls. Например, необходимо использовать вызов метода для выражения запроса, который возвращает число элементов, соответствующих указанным критериям.For example, you must use a method call to express a query that retrieves the number of elements that match a specified condition. Вызов метода также необходимо использовать для запроса, который получает элемент с максимальным значением в исходной последовательности.You also must use a method call for a query that retrieves the element that has the maximum value in a source sequence. В справочной документации по стандартным операторам запросов в пространствах имен System.Linq обычно применяется синтаксис методов.The reference documentation for the standard query operators in the System.Linq namespace generally uses method syntax. В связи с этим даже на начальном этапе работы с запросами LINQLINQ полезно иметь представление о том, как использовать синтаксис методов в самих запросах и выражениях запросов.Therefore, even when getting started writing LINQLINQ queries, it is useful to be familiar with how to use method syntax in queries and in query expressions themselves.

Методы расширения стандартных операторов запросовStandard Query Operator Extension Methods

В следующем примере показано простое выражение запроса и семантически эквивалентный ему запрос, написанный как запрос, основанный на методе.The following example shows a simple query expression and the semantically equivalent query written as a method-based query.

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
 */

Оба примера дают одинаковый результат.The output from the two examples is identical. Видно, что тип переменной запроса в обеих формах одинаковый: IEnumerable<T>.You can see that the type of the query variable is the same in both forms: IEnumerable<T>.

Чтобы разобраться в запросе, основанном на методе, изучим его более подробно.To understand the method-based query, let's examine it more closely. Обратите внимание на то, что в правой части выражения предложение where теперь выражается как метод экземпляра в объекта numbers, который, как вы, наверное, помните, имеет тип IEnumerable<int>.On the right side of the expression, notice that the where clause is now expressed as an instance method on the numbers object, which as you will recall has a type of IEnumerable<int>. Если вы знакомы с универсальным интерфейсом IEnumerable<T>, то знаете, что в нем отсутствует метод Where.If you are familiar with the generic IEnumerable<T> interface, you know that it does not have a Where method. При этом, вызвав список завершения IntelliSense в Visual Studio IDE, вы увидите не только метод Where, но и многие другие методы, такие как Select, SelectMany, Join и Orderby.However, if you invoke the IntelliSense completion list in the Visual Studio IDE, you will see not only a Where method, but many other methods such as Select, SelectMany, Join, and Orderby. Все это — стандартные операторы запросов.These are all the standard query operators.

Снимок экрана, показывающий все стандартные операторы запросов в Intellisense.

Может показаться, что класс IEnumerable<T> был переопределен и включает дополнительные методы, однако это не так.Although it looks as if IEnumerable<T> has been redefined to include these additional methods, in fact this is not the case. Стандартные операторы запросов реализуются как новый тип метода, который называется методы расширения.The standard query operators are implemented as a new kind of method called extension methods. Эти методы "расширяют" существующий тип и могут вызываться так, как если бы они являлись методами экземпляра для этого типа.Extensions methods "extend" an existing type; they can be called as if they were instance methods on the type. Стандартные операторы запроса расширяют IEnumerable<T>, и поэтому вы можете написать numbers.Where(...).The standard query operators extend IEnumerable<T> and that is why you can write numbers.Where(...).

Чтобы приступить к использованию LINQLINQ, о методах расширения достаточно знать только то, как ввести их в область действия в приложении, используя директивы using.To get started using LINQLINQ, all that you really have to know about extension methods is how to bring them into scope in your application by using the correct using directives. С точки зрения приложения метод расширения и обычные методы экземпляров одинаковы.From your application's point of view, an extension method and a regular instance method are the same.

Дополнительные сведения о методах расширения см. в разделе Методы расширения.For more information about extension methods, see Extension Methods. Дополнительные сведения о стандартных операторах запросов см. в разделе Общие сведения о стандартных операторах запроса (C#).For more information about standard query operators, see Standard Query Operators Overview (C#). Некоторые поставщики LINQLINQ, такие как LINQ to SQLLINQ to SQL и LINQ to XMLLINQ to XML, помимо IEnumerable<T> реализуют собственные стандартные операторы запросов и дополнительные методы расширения для других типов.Some LINQLINQ providers, such as LINQ to SQLLINQ to SQL and LINQ to XMLLINQ to XML, implement their own standard query operators and additional extension methods for other types besides IEnumerable<T>.

Лямбда-выраженияLambda Expressions

В предыдущем примере обратите внимание на то, что условное выражение (num % 2 == 0) передается в метод Where как встроенный аргумент: Where(num => num % 2 == 0).. Это встроенное выражение называется лямбда-выражением.In the previous example, notice that the conditional expression (num % 2 == 0) is passed as an in-line argument to the Where method: Where(num => num % 2 == 0). This inline expression is called a lambda expression. Это удобный способ написания кода, который иначе пришлось бы записывать более громоздко: как анонимный метод, универсальный метод-делегат или дерево выражения.It is a convenient way to write code that would otherwise have to be written in more cumbersome form as an anonymous method or a generic delegate or an expression tree. В C# => представляет собой лямбда-оператор, который читается как "переходит в".In C# => is the lambda operator, which is read as "goes to". num слева от оператора — входная переменная, которая соответствует переменной num в выражении запроса.The num on the left of the operator is the input variable which corresponds to num in the query expression. Компилятор может вывести тип num, поскольку известно, что numbers является универсальным типом IEnumerable<T>.The compiler can infer the type of num because it knows that numbers is a generic IEnumerable<T> type. Тело лямбда-выражения — точно такое же, как выражение в синтаксисе запроса или в любом другом выражении или операторе C#, и может включать вызовы метода и другие сложные логические выражения.The body of the lambda is just the same as the expression in query syntax or in any other C# expression or statement; it can include method calls and other complex logic. "Возвращаемое значение" — результат выражения.The "return value" is just the expression result.

Чтобы приступить к использованию LINQLINQ, активно использовать лямбда-выражения необязательно.To get started using LINQLINQ, you do not have to use lambdas extensively. При этом одни запросы могут быть выражены с помощью синтаксиса запроса, в то время как другие требуют лямбда-выражений.However, certain queries can only be expressed in method syntax and some of those require lambda expressions. После знакомства с лямбда-выражениями станет понятно, что они являются мощными и гибкими элементами в арсенале элементов LINQLINQ.After you become more familiar with lambdas, you will find that they are a powerful and flexible tool in your LINQLINQ toolbox. Дополнительные сведения см. в разделе Лямбда-выражения.For more information, see Lambda Expressions.

Совместимость запросовComposability of Queries

Обратите внимание на то, что в представленном выше примере кода метод OrderBy вызывается с помощью оператора точки при вызове Where.In the previous code example, note that the OrderBy method is invoked by using the dot operator on the call to Where. Where создает отфильтрованную последовательность, а затем Orderby ее сортирует.Where produces a filtered sequence, and then Orderby operates on that sequence by sorting it. Поскольку запросы возвращают IEnumerable, объедините их в синтаксис метода, собрав вызовы методов в цепочку.Because queries return an IEnumerable, you compose them in method syntax by chaining the method calls together. Компилятор выполняет это действие в фоновом режиме, когда вы пишете запросы, используя синтаксис запросов.This is what the compiler does behind the scenes when you write queries by using query syntax. А поскольку в переменной запроса результаты запроса не сохраняются, его можно в любое время изменить или использовать как базу для нового запроса даже после выполнения.And because a query variable does not store the results of the query, you can modify it or use it as the basis for a new query at any time, even after it has been executed.