Another two types of Syntax are available when writing a LINQ Query: Fluent Syntax and Query Expression ).
The syntax of the LINQ method is very flexible and important. Here we will describe how to use the link query operator to create complex queries, the essence of method syntax is to create a query by using extension methods and Lambda expressions. C #3.0 introduces the declarative query syntax for the LINQ expression. The query written by the query syntax is similar to the SQL query. This article will introduce the syntax of the LINQ method in detail.
Of course, the. NET public Language Runtime Library (CLR) does not have the concept of query syntax. Therefore, the compiler converts the query expression to the method syntax during program compilation, that is, calling the extension method. Therefore, using method syntax will bring us closer to and understand the implementation and nature of LINQ, and some queries can only be expressed as method calls, such as querying the maximum and minimum values of elements in the search sequence, they have no corresponding implementation in the query syntax. On the other hand, the query syntax is usually relatively simple and easy to read. In any case, these two syntaxes are complementary and compatible with each other. We can mix method syntax and query syntax in a query.
Link query operator
In this section, we demonstrate how to create a query using a single query operator. To create more complex queries, we can add other query operators after the expressions to generate a query chain. For example, query all the names with the letter "a", sort the names by length, and convert all the results to the uppercase/lowercase format.
static void Main(string[] args)
{
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
IEnumerable<string> query = names
.Where(n => n.Contains("a"))
.OrderBy(n => n.Length)
.Select(n => n.ToUpper());
foreach (string name in query) 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, and the result forms a transmission chain of sequence ,:
Figure: link query operator
The Where, OrderyBy, and Select operators correspond to the corresponding extension methods in the Enumerable class. Where generates a filtered sequence; OrderBy generates the sort version of the input sequence; each element in the Select sequence is converted by the given lambda expression.
The following is the signature of the Where, OrderBy, and Select extension methods:
public static IEnumerable<TSource> Where<TSource>
(this IEnumerable<TSource> source, Func<TSource, bool> 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)
Importance of extension methods
In a LINQ expression, it is the extension method that makes the link of the LINQ query operator possible. Because the operator itself is an extension of the IEnumerable <T> type and returns the result of the IEnumerable <T> type. We can compare the differences between using the extension method and using the static method, and the results will be clear at a glance. The extension method naturally reflects the data flow from left to right, while maintaining the consistency between lambda expressions and query operators.
// extension methods make LINQ elegant
IEnumerable<string> query = names
.Where(n => n.Contains("a"))
.OrderBy(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 preceding example, we provide the Where operator with the following Lambda expression: n => n. Contains (""). Lambda expressions have different purposes for different query operators. For Where, it determines whether an element is included in the result sequence; For OrderBy, it maps each element to a comparison key value; for Select, lambda expressions determine the conversion of elements in the input sequence and then put them into the output sequence.
For a detailed introduction to Lambda expressions, refer to the language functions of C #3.0 (below ).
Generally, the query operator calls the Lambda expression provided by us for each element in the input sequence. This gives us a chance to implement our own logic and get the expected results. Let's take a look at the implementation of. NET Framework for Enumerable. Where:
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
foreach (TSource element in source)
if (predicate(element))
yield return element;
}
The standard query operator uses A common Func delegate. Func is A group defined in System. Linq (Thanks A _ ming ~ Always correct, here should be the general delegate in the System) namespace. It accepts a series of input parameters and a return value, and the return value corresponds to the last Parameter definition. Therefore, the Func <TSource, 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 operator uses the same type name:
Common Type name |
Purpose |
TSource |
Element type in Input sequence |
TResult |
Element type in Output sequence (if not the same as TSource) |
TKey |
Use sorting, grouping, joining, and other key value types |
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 TSource => TResult Lambda expression, 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 an element of the string type to an element of the int type:
Static void TestSelectOperator (){
String [] names = {"Tom", "Dick", "Harry", "Mary", "Jay "};
// The compiler will infer from the Lambda expression n => n. Length that the TResult is of the int type.
IEnumerable <int> query = names. Select (n => n. Length );
Foreach (int length in query)
Console. Write (length + "|"); // 3 | 4 | 5 | 4 | 3
}
For the Where query operator, it does not need to deduce the type of the output element, because it only filters the input elements without conversion, therefore, the output element has the same data type as the input element.
For the OrderBy query operator, Func <TSource, TKey> maps the input element to a sort key value. TKey is inferred from the Lambda expression results. For example, we can sort the names array by length or in alphabetical order:
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
IEnumerable<string> sortedByLength, sortedAlphabetically;
sortedByLength = names.OrderBy(n => n.Length); // int key
sortedAlphabetically = names.OrderBy(n => n); // string key
Other query Operators
Not all query operators return a sequence. The element operator obtains a single element from the input sequence, such as First, Last, and ElementAt:
int[] numbers = { 10, 9, 8, 7, 6 };
int firstNumber = numbers.First(); // 10
int lastNumber = numbers.Last(); // 6
int secondNumber = numbers.ElementAt(1); // 9
int lowestNumber = numbers.OrderBy(n => n).First(); // 6
The aggregation operator returns a scalar value, usually a numerical value:
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 hasAnOddElement = numbers.Any(n => (n % 2) == 1); //true
Because these operators do not return a sequence, we cannot connect these operators to other operators. In other words, they usually appear at the end of the query.
Some query operators accept two input sequence. For example, Concat adds a sequence to another sequence. Union is similar to Concat, but the same element is removed:
int[] seq1 = { 1, 2, 2, 3 };
int[] seq2 = { 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 common query operators. In the subsequent chapters, I will discuss more operators in detail.
Blog series Navigation:
Blog navigation of the road to LINQ
Road 1 of LINQ: Introduction to LINQ
The path to LINQ 2: C #3.0 language functions (I)
The path to LINQ 3: C #3.0 language functions (II)
The road to LINQ 4: the syntax of the LINQ Method
The path to LINQ 5: a linq query expression
6: latency in Execution (Deferred Execution)
7: subquery, creation policy, and Data Conversion
8: Explain query (Interpreted Queries)
Road 9: LINQ to SQL and Entity Framework (I)
10: The Path to LINQ to SQL and Entity Framework (below)
11: Filtering by LINQ Operators)
12: Data Conversion (Projecting) of LINQ Operators)
Connection to the LINQ road 13: connection to the LINQ Operators (Joining)
The road to LINQ 14: sorting and Grouping of LINQ Operators (Ordering and Grouping)
Road 15 to LINQ: Element Operators, set methods, and quantifiers of LINQ Operators
LINQ LINQ 16: Collection Operators, Zip Operators, conversion methods, and generator methods of LINQ Operators
X-DOM introduction to the road to LINQ 17: LINQ to XML;
18: navigation and query of LINQ to XML
Road 19: X-DOM update and interaction with Value attributes for LINQ to XML
Path 20 to LINQ to XML: Documents, Declarations, and Namespaces
The path to LINQ 21: The X-DOM for the generation of LINQ to XML (Projecting)
Post of the series of blogs on the road to LINQ