query-expression: from-clause query-body from-clause: from from-generators from-generators: from-generator from-generators , from-generator from-generator: identifier in expression query-body: from-or-where-clausesopt orderby-clauseopt select-or-group-clause into-clauseopt from-or-where-clauses: from-or-where-clause from-or-where-clauses from-or-where-clause from-or-where-clause: from-clause where-clause where-clause: where boolean-expression orderby-clause: orderby ordering-clauses ordering-clauses: ordering-clause ordering-clauses , ordering-clause ordering-clause: expression ordering-directionopt ordering-direction: ascending descending select-or-group-clause: select-clause group-clause select-clause: select expression group-clause: group expression by expression into-clause: into identifier query-bodyA query-expression is classified as a non-assignment-expression, the definition of which occurs in §26.3.
A query expression begins with a from clause and ends with either a select or group clause. The initial from clause can be followed by zero or more from or where clauses. Each from clause is a generator that introduces an iteration variable ranging over a sequence, and each where clause is a filter that excludes items from the result. The final select or group clause specifies the shape of the result in terms of the iteration variable(s). The select or group clause may be preceded by an orderby clause that specifies an ordering for the result. Finally, an into clause can be used to "splice" queries by treating the results of one query as a generator in a subsequent query.
In a query expression, a from clause with multiple generators is exactly equivalent to multiple consecutive from clauses with a single generator.
Query Expression Translation
The C# 3.0 language does not specify the exact execution semantics of query expressions. Rather, C# 3.0 translates query expressions into invocations of methods that adhere to the query expression pattern. Specifically, query expressions are translated into invocations of methods named Where, Select, SelectMany, OrderBy, OrderByDescending, ThenBy, ThenByDescending, and GroupBy that are expected to have particular signatures and result types, as described in §26.7.2. These methods can be instance methods of the object being queried or extension methods that are external to the object, and they implement the actual execution of the query.The translation from query expressions to method invocations is a syntactic mapping that occurs before any type binding or overload resolution has been performed. The translation is guaranteed to be syntactically correct, but it is not guaranteed to produce semantically correct C# code. Following translation of query expressions, the resulting method invocations are processed as regular method invocations, and this may in turn uncover errors, for example if the methods do not exist, if arguments have wrong types, or if the methods are generic and type inference fails.
The translation of query expressions is demonstrated through a series of examples in the following. A formal description of the translation rules is provided in a later section.
where clauses
A where clause in a query expression:
from c in customers where c.City == "London" select ctranslates to an invocation of a Where method with a synthesized lambda expression created by combining the iteration variable identifier and the expression of the where clause:
customers. Where(c => c.City == "London")select clauses
The example in the previous section demonstrates how a select clause that selects the innermost iteration variable is erased by the translation to method invocations.
A select clause that selects something other than the innermost iteration variable:
from c in customers where c.City == "London" select c.Nametranslates to an invocation of a Select method with a synthesized lambda expression:
customers. Where(c => c.City == "London"). Select(c => c.Name)group clauses
A group clause:
from c in customers group c.Name by c.Countrytranslates to an invocation of a GroupBy method:
customers. GroupBy(c => c.Country, c => c.Name)orderby clauses
An orderby clause:
from c in customers orderby c.Name select new { c.Name, c.Phone }translates to an invocation of an OrderBy method, or an OrderByDescending method if a descending direction was specified:
customers. OrderBy(c => c.Name). Select(c => new { c.Name, c.Phone })Secondary orderings in an orderby clause:
from c in customers orderby c.Country, c.Balance descending select new { c.Name, c.Country, c.Balance }translate to invocations of ThenBy and ThenByDescending methods:
customers. OrderBy(c => c.Country). ThenByDescending(c => c.Balance). Select(c => new { c.Name, c.Country, c.Balance })Multiple generators
Multiple generators:
from c in customers where c.City == "London" from o in c.Orders where o.OrderDate.Year == 2005 select new { c.Name, o.OrderID, o.Total }translate to invocations of SelectMany for all but the innermost generator:
customers. Where(c => c.City == "London"). SelectMany(c => c.Orders. Where(o => o.OrderDate.Year == 2005). Select(o => new { c.Name, o.OrderID, o.Total }) )When multiple generators are combined with an orderby clause:
from c in customers, o in c.Orders where o.OrderDate.Year == 2005 orderby o.Total descending select new { c.Name, o.OrderID, o.Total }an additional Select is injected to collect the ordering expressions and the final result in a sequence of tuples. This is necessary such that OrderBy can operate on the entire sequence. Following OrderBy, the final result is extracted from the tuples:
customers. SelectMany(c => c.Orders. Where(o => o.OrderDate.Year == 2005). Select(o => new { k1 = o.Total, v = new { c.Name, o.OrderID, o.Total } }) ). OrderByDescending(x => x.k1). Select(x => x.v)into clauses
An into clause:
from c in customers group c by c.Country into g select new { Country = g.Key, CustCount = g.Group.Count() }is simply a more convenient notation for a nested query:
from g in from c in customers group c by c.Country select new { Country = g.Key, CustCount = g.Group.Count() }the translation of which is:
customers. GroupBy(c => c.Country). Select(g => new { Country = g.Key, CustCount = g.Group.Count() })
The Query Expression Pattern
The Query Expression Pattern establishes a pattern of methods that types can implement to support query expressions. Because query expressions are translated to method invocations by means of a syntactic mapping, types have considerable flexibility in how they implement the query expression pattern. For example, the methods of the pattern can be implemented as instance methods or as extension methods because the two have the same invocation syntax, and the methods can request delegates or expression trees because lambda expressions are convertible to both.The recommended shape of a generic type C
delegate R Func(A arg); class C{ public C Where(Func predicate); public C Select(Funcselector); public C SelectMany(Func> selector); public OOrderBy (Func keyExpr); public O OrderByDescending (Func keyExpr); public C > GroupBy (Func keyExpr); public C > GroupBy (Func keyExpr, Func elemExpr); } class O : C { public O ThenBy (Func keySelector); public O ThenByDescending (Func keySelector); } class G { public K Key { get; } public C Group { get; } }
Formal Translation Rules
A query that contains an into clause
q1 into x q2
from x in ( q1 ) q2
A from clause with multiple generators
from g1 , g2 , ... gn
from g1 from g2 ... from gn
A from clause immediately followed by a where clause
from x in e where f
from x in ( e ) . Where ( x => f )
A query expression with multiple from clauses, an orderby clause, and a select clause
from x1 in e1 from x2 in e2 ... orderby k1 , k2 ... select v
( from x1 in e1 from x2 in e2 ... select new { k1 = k1 , k2 = k2 ... , v = v } ) . OrderBy ( x => x . k1 ) . ThenBy ( x => x . k2 ) ... . Select ( x => x . v )
A query expression with multiple from clauses, an orderby clause, and a group clause
from x1 in e1 from x2 in e2 ... orderby k1 , k2 ... group v by g
( from x1 in e1 from x2 in e2 ... select new { k1 = k1 , k2 = k2 ... , v = v , g = g } ) . OrderBy ( x => x . k1 ) . ThenBy ( x => x . k2 ) ... . GroupBy ( x => x . g , x => x . v )
A query expression with multiple from clauses and a select clause
from x in e from x1 in e1 ... select v
( e ) . SelectMany ( x => from x1 in e1 ... select v )
A query expression with multiple from clauses and a group clause
from x in e from x1 in e1 ... group v by g
( e ) . SelectMany ( x => from x1 in e1 ... group v by g )
A query expression with a single from clause, no orderby clause, and a select clause
from x in e select v
( e ) . Select ( x => v )
( e )
A query expression with a single from clause, no orderby clause, and a group clause
from x in e group v by g
( e ) . GroupBy ( x => g , x => v )
( e ) . GroupBy ( x => g )
A query expression with a single from clause, an orderby clause, and a select clause
from x in e orderby k1 , k2 ... select v
( e ) . OrderBy ( x => k1 ) . ThenBy ( x => k2 ) ... . Select ( x => v )
( e ) . OrderBy ( x => k1 ) . ThenBy ( x => k2 ) ...
A query expression with a single from clause, an orderby clause, and a group clause
from x in e orderby k1 , k2 ... group v by g
is translated into
( e ) . OrderBy ( x => k1 ) . ThenBy ( x => k2 ) ... . GroupBy ( x => g , x => v )
except when v is the identifier x, the translation is
( e ) . OrderBy ( x => k1 ) . ThenBy ( x => k2 ) ... . GroupBy ( x => g )
Expression Trees
Funcf = x => x + 1; // Code Expression > e = x => x + 1; // Data
No comments:
Post a Comment