(Article) Understand How Query Expressions Map to Method Calls
LINQ is built on two concepts: A query language and a translation from that query language to a set of methods. Every query expression has a mapping to some method call or calls. You should understand this mapping from two different perspectives: From the perspective of a class user and as a class designer.
The C# compiler converts query expressions in that query language into method calls. Every query expression has a mapping to some method call or calls. You should understand this mapping from two different perspectives. From the perspective of a class user, you need to understand that your query expressions are nothing more than method calls. A where clause translates to a call to a method named Where(), with the proper set of parameters. As a class designer, you should evaluate the implementations of those methods provided by the base framework and determine if you can create better implementations for your types. If not, you should simply defer to the base library versions. However, when you can create a better version, you must make sure that you fully understand the translation from query expressions into method calls. It’s your responsibility to ensure that your method signatures correctly handle every translation case. For some of the query expressions, that’s rather obvious. However, a couple of the more complicated expressions are a little more difficult to comprehend.
The full Query Expression Pattern contains eleven different methods. The following is the definition from the C# language Specification, §7.15.3 (reprinted with permission from Microsoft Corporation):
delegate R Func<T1,R>(T1 arg1);
delegate R Func<T1,T2,R>(T1 arg1, T2 arg2);
class C
{
public C<T> Cast<T>();
}
class C<T> : C
{
public C<T> Where(Func<T,bool> predicate);
public C<U> Select<U>(Func<T,U> selector);
public C<V> SelectMany<U,V>(Func<T,C<U>> selector,
Func<T,U,V> resultSelector);
public C<V> Join<U,K,V>(C<U> inner, Func<T,K>
outerKeySelector,
Func<U,K> innerKeySelector, Func<T,U,V> resultSelector);
public C<V> GroupJoin<U,K,V>(C<U> inner,
Func<T,K>
outerKeySelector,
Func<U,K> innerKeySelector, Func<T,C<U>,V> resultSelector);
public O<T> OrderBy<K>(Func<T,K> keySelector);
public O<T> OrderByDescending<K>(Func<T,K> keySelector);
public C<G<K,T>> GroupBy<K>(Func<T,K> keySelector);
public C<G<K,E>> GroupBy<K,E>(Func<T,K> keySelector,
Func<T,E> elementSelector);
}
class O<T> : C<T>
{
public O<T> ThenBy<K>(Func<T,K> keySelector);
public O<T> ThenByDescending<K>(Func<T,K> keySelector);
}
class G<K,T> : C<T>
{
public K Key { get; }
}
The .NET base library provides two general purpose reference implementations of this pattern. System.Linq.Enumerable provides extension methods on IEnumerable<T> that implement the query expression pattern. System.Linq.Queryable provides a similar set of extension method on IQueryable<T> that support a query provider’s ability to translate queries into some other format for execution. (For example, the LINQ to SQL implementation converts query expressions to SQL queries that are executed by the SQL database engine). As a class user, that means you are probably using one of those two reference implementations for most of your queries. Secondly, as a class author, it means that if you create a data source that implements IEnumerable<T>, or IQueryable<T> (or a closed generic type from IEnumerable<T> or IQueryable<T>) they your type already implements the Query Expression Pattern. You’re type has that implementation using the extension methods defined in the base library.
courtesy: www.informit.com

