- The standard query operators are the methods that form the Language-Integrated Query (LINQ) pattern.
- Most of these methods operate on sequences of object whose type implements the
IEnumerable<T>
or the IQueryable<T>
.
- The additional functionality is provided using extension methods in
System.Linq.Enumerable
class.
- The standard query operators differ in the timing of their execution, depending on whether they return a singleton value or a sequence of values.
- Those methods that return a singleton value (Average, Sum...) execute immediately.
- Methods that return a sequence defer the query execution and return an enumerable object.
- Methods that extend
IEnumerable<T>
, the returned enumerable object captures the arguments that were passed to the method.
- When that object is enumerated, the logic of the query operator is employed and the query results are returned.
- Methods that extend
IQueryable<T>
build an expression tree that represents the query to be performed.
// Split the string into individual words to create a collection.
string[] words = "the quick brown fox jumps over the lazy dog".Split(' ');
// Using query expression syntax.
var query = from word in words
group word.ToUpper() by word.Length into gr
orderby gr.Key
select new { Length = gr.Key, Words = gr };
// Using method-based query syntax.
var query2 = words
.GroupBy(w => w.Length, w => w.ToUpper())
.Select(g => new { Length = g.Key, Words = g })
.OrderBy(o => o.Length);
Deferred Execution
- A delegate expression that takes an argument and returns a Boolean is called a predicate.
- In general, predicates should do exactly one thing: evaluate a condition, and they should not have any side effects.
- In the context of LINQ and standard query operators, each lambda expression forms part of the overall query to be executed.
- At the time of declaration, lambda expressions do not execute. It isn’t until the lambda expressions are invoked that the code within them begins to execute.
- The call to
ToArray()
, ToList()
, ToDictionary()
, or ToLookup()
triggers the lambda expression for each item and brings the entire result set into memory.
- Furthermore, the “To” methods will snapshot the underlying data so that no fresh results will be returned upon re-querying the “To” method result.
- The query object represents the query, not the results.
- When asking the query for the results, the whole query executes.
Cast<TResult>()
& OfType<TResult>()
- The methods,
Cast<TResult>(IEnumerable)
and OfType<TResult>(IEnumerable)
, let you enable a non-parameterized, or non-generic, collection to be queried in the LINQ pattern. They do this by creating a strongly-typed collection of objects.
- Both of these methods are implemented by using deferred execution. Their immediate return value is an object that stores all the information that is required to perform the action. The query represented by these methods are not executed until the object is enumerated either by calling its
GetEnumerator()
method directly or by using foreach.
- Cast<TResult>() helps to change the type of a sequence, when we expect all the items of the sequence to be of a specific type.
- When a sequence contains many different types, and we are only concerned with a subset of a given type, we can
OfType<TResult>()
extension method.
var list = new List<DerivedClass>();
IEnumerable<BaseClass> baseClassList1 = list.Cast<BaseClass>();
IEnumerable<BaseClass> baseClassList2 = list.OfType<BaseClass>();
First()
& Single()