Delayed execution of LINQ

Source: Internet
Author: User
ArticleDirectory
    • Repeated execution
    • Variable capture
    • Implementation principle of delayed execution

Most query operators in LINQ have a very important feature: delayed execution. This means that they are not executed during query creation, but during traversal (in other words, when the movenext method of enumerator is called ). Let's consider the following query:

Static VoidTestdeferredexecution ()
{
VaRNumbers =NewList <Int> ();
Numbers. Add (1);
Ienumerable <Int> Query = numbers. Select (n => N *10);//Build Query

Numbers. Add (2);//Add an extra element after the query
Foreach(IntNInQuery)
Console. Write (n +"|");//10 | 20 |
}

It can be seen that the number we added after the query is created is also included in the query results, because the query will not be executed until the foreach statement repeats the query, the data source numbers already contains the later added element 2. This feature of LINQ is delayed execution. Except the following two query operators, all other operators are delayed:

    • Returns the query operators of a single element or scalar value, such as first and count.
    • The following conversion operators: toarray, tolist, todictionary, and tolookup.

The preceding two operators will be executed immediately because their return value type does not provide a mechanism for delayed execution. For example, the following query will be executed immediately.

 
IntMatches = numbers. Where (n => (N %2) =0). Count ();//1

For LINQ, delayed execution is very important because it decouples query creation from query execution, which allows us to look like creating an SQL query, you can use multiple steps to create a join query.

Repeated execution

One of the effects of delayed execution is that when we traverse the query results repeatedly, the query will be executed repeatedly:

Static VoidTestreevaluation ()
{
VaRNumbers =NewList <Int> (){1,2};

Ienumerable <Int> Query = numbers. Select (n => N *10);//Build Query
Foreach(IntNInQuery) console. Write (n +"|");//10 | 20 |

Numbers. Clear ();
Foreach(IntNInQuery) console. Write (n +"|");//<Nothing>
}

Sometimes repeated execution is not an advantage for us. The reasons are as follows:

    • When we need to save the query results at a given vertex.
    • Some queries are time-consuming. For example, when querying a very large sequence or obtaining data from a remote database, we do not want a query to be executed repeatedly for performance considerations.

At this time, we can use the previously introduced conversion operators, such as toarray and tolist, to avoid repeated execution. toarray saves the query results to an array, while tolist saves the result to the generic list <>:

 
Static VoidTestdefeatreevaluation ()
{
VaRNumbers =NewList <Int> (){1,2};

List <Int> TimesTen = numbers
. Select (n => N *10)
. Tolist ();//Executes immediately into a list <int>

Numbers. Clear ();
Console. Write (TimesTen. Count );//Still 2
}
Variable capture

Delayed execution also has a bad side effect. If the queried Lambda expression referencesProgramLocal variable, the query will capture the variable during execution. This means that if the value of the variable is changed after the query definition, the query results will also change.

Static VoidTestcapturedvariable ()
{
Int[] Numbers = {1,2};

IntFactor =10;
Ienumerable <Int> Query = numbers. Select (n => N * factor );
Factor =20;
Foreach(IntNInQuery)
Console. Write (n +"|");//20 | 40 |
}

This feature becomes a real trap when we create a query through the foreach loop. If we want to remove all the vowels in a string, we may write the following query:

Ienumerable <Char> Query ="How are you, friend.";

Query = query. Where (C => C! ='A');
Query = query. Where (C => C! ='E');
Query = query. Where (C => C! ='I');
Query = query. Where (C => C! ='O');
Query = query. Where (C => C! ='U');

Foreach(CharCInQuery) console. Write (C );//Hw r y, frnd.

Although the program results are correct, we can see that such a program is not elegant enough. So we will naturally think of using the foreach loop to refactor the above program:

Ienumerable <Char> Query ="How are you, friend.";

Foreach(CharVowelIn "Aeiou")
Query = query. Where (C => C! = Vowel );

Foreach(CharCInQuery) console. Write (C );//How are yo, friend.

In the result, only the letter U is filtered out. Are you surprised!However, you only need to think carefully about the cause: Because vowel is defined outside the loop, each Lambda expression captures the same variable. What is the vowel value when we execute a query? Not exactly the letter U to be filtered. To solve this problem, we only need to assign the cyclic variable to an internal variable. The following temp variable scope is only the current Lambda expression..

Ienumerable <Char> Query ="How are you, friend.";

Foreach(CharVowelIn "Aeiou")
{
CharTemp = vowel;
Query = query. Where (C => C! = Temp );
}

Foreach (char I in "aeiou ")
{
Query = query. Where (n => n! = I). toarray ();// This is also possible
}

 

Foreach(CharCInQuery) console. Write (C );//Hw r y, frnd.
Implementation principle of delayed execution

The query operator supports delayed execution by returning the decorator sequence.

Unlike traditional set types such as array and linked list, a sequence of a decorator does not have its own underlying structure for storing elements, but encapsulates another sequence we provide at runtime. When we request data from the decorator sequence, it will request data from the encapsulated sequence.

For example, you can call where to create a sequence decorator, which stores the reference of the input sequence, lambda expressions, and other provided parameters. The following query shows the corresponding decorator sequence:

 
Ienumerable <Int> Lessthanten =New Int[] {5,12,3}. Where (n => n <10);

When we traverse lessthanten, we are actually searching for data from the array through the where modifier.

The query operator LINK creates a multi-layer decorator. Each query operator instantiates a decorator to wrap the previous sequence, such as the following query and the corresponding multi-layer decorator sequence:

 
Ienumerable <Int> Query =New Int[] {5,12,3}
. Where (n => n <10)
. Orderby (n => N)
. Select (n => N *10);

When we traverse the query, we are actually querying the original array through a modifier chain.

Note that if a conversion operator such as tolist is added after the preceding query, the query will be executed immediately, so that a single list will replace the entire object model above.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.