Dela via


LINQ Query Syntax versus Method Syntax (C#)

Most queries in the introductory LINQ documentation are written as query expressions by using the declarative query syntax introduced in C# 3.0. However, the .NET common language runtime (CLR) has no notion of query syntax in itself. Therefore, at compile time, query expressions are translated to something that the CLR does understand: method calls. These methods are called the standard query operators, and they have names such as Where, Select, GroupBy, Join, Max, Average, and so on. You can call them directly by using method syntax instead of query syntax.

In general, we recommend query syntax because it is usually simpler and more readable; however there is no semantic difference between method syntax and query syntax. In addition, some queries, such as those that retrieve the number of elements that match a specified condition, or that retrieve the element that has the maximum value in a source sequence, can only be expressed as method calls. The reference documentation for the standard query operators in the System.Linq namespace generally uses method syntax. Therefore, even when getting started writing LINQ 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. 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. 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>. If you are familiar with the generic IEnumerable<T> interface, you know that it does not have a Where method. 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.

Standard Query Operators in Intellisense

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. The standard query operators extend IEnumerable<T> and that is why you can write numbers.Where(...).

To get started using LINQ, 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. This is explained additionally in How to: Create a LINQ Project. 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# Programming Guide). For more information about standard query operators, see Standard Query Operators Overview. Some LINQ providers, such as LINQ to SQL and LINQ to XML, implement their own standard query operators and additional extension methods for other types besides IEnumerable<T>

Lambda Expressions

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. In C# => is the lambda operator, which is read as "goes to". The num on the left of the operator is the input variable which corresponds to num in the query expression. The compiler can infer the type of num because it knows that numbers is a generic IEnumerable<T> type. 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.

To get started using LINQ, 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. After you become more familiar with lambdas, you will find that they are a powerful and flexible tool in your LINQ toolbox. For more information, see Lambda Expressions (C# Programming Guide).

Composability of Queries

In the previous code example, note that the OrderBy method is invoked by using the dot operator on the call to Where. Where produces a filtered sequence, and then Orderby operates on that sequence by sorting it. 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.

See Also

Other Resources

Getting Started with LINQ in C#