There are two more syntaxes to choose from when writing LINQ queries: The method Syntax (Fluent Syntax) and the query Expression syntax.
The LINQ method syntax is very flexible and important, and here we describe how to create complex queries using the link query operator, the essence of which is to create queries through extension methods and lambda expressions. C # 3.0 also introduces declarative query syntax for LINQ expressions, and queries written out with query syntax are more similar to SQL queries. This article provides a detailed introduction to the LINQ method syntax.
Of course. The net common language runtime (CLR) does not have the concept of query syntax. Therefore, the compiler converts the query expression to the method syntax, called the extension method, at program compile time. So using method syntax allows us to get closer to and understand the implementation and nature of LINQ, and some queries can only be represented as method invocations, such as queries that retrieve the maximum and minimum elements of a sequence, and they do not have a corresponding implementation in the query syntax. On the other hand, the query syntax is generally simpler and easier to read. Anyway, these two grammars are complementary and compatible, and we can mix the method syntax and query syntax in a single query.
Link Query operators
In the introduction of LINQ, we demonstrated a query created using a single query operator. If you need to create more complex queries, we can add additional query operators after the expression, resulting in a query chain. In the following example: All names containing the letter "a" are queried, sorted by length, and all the results are converted to uppercase format.
Static voidMain (string[] args)
{
string[] names = {"Tom","Dick","Harry","Mary","Jay"};
ienumerable<string> query = Names
. Where (n = n.contains ("a"))
. (n = n.length)
. Select (n = n.toupper ());
foreach(stringNameinchQuery) Console.WriteLine (name);
}
//Result:
JAY
MARY
HARRY
As shown in this example, when a link uses a query operator, the output sequence of an operator becomes the input sequence of the next operator, resulting in a sequence transport chain:
Figure: Link Query operators
Where, orderyby, select these standard query operators correspond to the corresponding extension methods in the enumerable class. where produces a filtered Sequence;orderby generates a sorted version of the input sequence, and each element in the sequence that the select obtains is transformed by a given lambda expression.
Here are the signatures of the where, by-do, select extension methods:
Public Static Ienumerable<tsource> where<tsource>
(Thisbool> predicate)
Public Static Ienumerable<tsource> Orderby<tsource, tkey>
( This ienumerable<tsource> source, Func<tsource, tkey> keyselector)
Public Static Ienumerable<tresult> Select<tsource, tresult>
( This ienumerable<tsource> source, Func<tsource, tresult> selector)
The importance of extension methods
In a LINQ expression, it is the extension method that makes the link to the LINQ query operator possible. Because the operator itself is an extension to the ienumerable<t> type, and returns the result of the ienumerable<t> type. We can compare the differences between using extension methods and using static methods, and the results will be at a glance. The extension method is a natural reflection of left-to-right data flow, while preserving the position consistency of the lambda expression and the query operator.
//extension methods make LINQ elegant
ienumerable<string> query = Names
. Where (n = n.contains ("a"))
. (n = n.length)
. Select (n = n.toupper ());
//static methods lose query ' s fluency
ienumerable<string> Query2 =
Enumerable.select (
Enumerable.orderby (
Enumerable.where (names, n = n.contains ("a")
), n = n.length
), n = N.toupper ()
);
Create a lambda expression
In the example above, we provided the following lambda expression to the WHERE operator: n = n.contains ("a"). For each query operator, the purpose of the lambda expression is different. For where, it determines whether an element is contained in the result sequence, and for the by, it maps each element to the comparative key value; The lambda expression determines how the elements in the input sequence are converted and then put into the output sequence.
For a detailed description of lambda expressions, refer to the language features of the LINQ Route 3:c# 3.0 (below).
In general, the query operator invokes the lambda expression we provide for each element in the input sequence, giving us a chance to implement our own logic to get the results we need. We can look at the implementation of the. NET Framework for Enumerable.where:
public static ienumerable< Tsource> where<tsource> (
this ienumerable<tsource> Sour CE, Func<tsource, bool > predicate)
{
The standard query operator uses a generic Func delegate, and Func is a set of definitions in system. Linq(thank you, A_ ) is a generic delegate in the namespace of the system. It accepts a series of input parameters and a return value corresponding to the last parameter definition. So, Func<tsource, the bool> delegate matches the TSource = bool expression, accepts the TSource input parameter, and returns a bool value.
lambda expressions and element types
The standard query operators use a uniform type name:
Generic type name |
Use |
TSource |
Type of element in Input sequence |
TResult |
Element type in Output sequence (if not the same as TSource) |
TKey |
Key-value types using sorting, grouping, joining, etc. |
TSource is determined by the input sequence, while TResult and TKey are inferred from the lambda expression we provide.
For example: The signature of the Select query operator is as follows:
Public Static Ienumerable<tresult> select<tsource,tresult> (
This Ienumerable<tsource> source, func<tsource,tresult> selector)
Func<tsource,tresult> matches the lambda expression TSource = TResult, accepts an input parameter TSource, and returns TResult. Because TSource and TResult are different types, our lambda expressions can even change the data type of the INPUT element. The following example converts a string type element to an int type element:
Static voidTestselectoperator () {
string[] names = {"Tom","Dick","Harry","Mary","Jay"};
//The compiler infers the TResult to be of type int from the lambda expression n = n.length
ienumerable<int> query = names. Select (n = n.length);
foreach(intLengthinchQuery
Console.Write (length +"|");//3|4|5|4|3
}
For the Where query operator, it does not require type inference for the output element because it simply filters the input elements without conversion, so the output element and the INPUT element have the same data type.
For the order BY query operator, Func<tsource, tkey> maps the INPUT element to a sort key value. TKey is inferred from the results of the lambda expression, for example, we can sort the names array by length or alphabetical order:
string[] names = {"Tom","Dick","Harry","Mary","Jay"};
ienumerable<string> Sortedbylength, sortedalphabetically;
Sortedbylength = names. (n = n.length);//int Key
sortedalphabetically = names. (n = n);//string Key
Additional query operators
Not all query operators return a sequence. The element operator takes a single element from the input sequence, such as: First,last and ElementAt:
int[] numbers = {Ten,9,8,7,6};
intFirstnumber = numbers. First ();//Ten
intLastnumber = numbers. Last ();//6
intSecondnumber = numbers. ElementAt (1);//9
intLowestnumber = numbers. (n = n). First ();//6
The set (aggregation) operator returns a scalar value, usually a numeric type:
int count = numbers. Count (); // 5
int min = numbers. Min (); // 6
The judgment operator returns a bool value:
bool hasthenumbernine = numbers. Contains (9); // true
bool haselements = numbers. Any (); // true
BOOL 2 1); // true
Because these operators do not return a sequence, we cannot link other operators after these operators, in other words, they generally appear at the end of the query.
There are also some query operators that accept two input sequence, such as concat, which adds one sequence to the back of another sequence; Union is similar to concat but removes the same elements:
int 1 2 2 3 };
int 3 4 5 };
ienumerable<int> concat = seq1. Concat (SEQ2); // {1, 2, 2, 3, 3, 4, 5}
ienumerable<int> union = seq1. Union (SEQ2); // {1, 2, 3, 4, 5}
This article only introduces several commonly used query operators, and in subsequent chapters, I'll discuss more in more detail on the operators.
LINQ Path 4:linq method Syntax