About Me

My photo
Northglenn, Colorado, United States
I'm primarily a BI Developer on the Microsoft stack. I do sometimes touch upon other Microsoft stacks ( web development, application development, and sql server development).

Wednesday, January 23, 2008

LINQ's "Outer Variables in a Loop"

Started using LINQPad and going through the "C# 3.0 in a Nutshell" tutorial that came with the program.

Then I ran the following script from "Chapter 8 - LINQ Queries/Deferred Execution/Outer Variables in a Loop" :

IEnumerable<char> query = "Not what you might expect";

 

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');

 

new string (query.ToArray()).Dump ("All vowels are stripped, as you'd expect.");

 

query = "Not what you might expect";

 

foreach (char vowel in "aeiou")

{

    query = query.Where (c => c != vowel);

}

 

new string (query.ToArray()).Dump ("Notice that only the 'u' is stripped!");

 

with the following results:

▪ All vowels are stripped, as you'd expect.

Nt wht y mght xpct

▪ Notice that only the 'u' is stripped!

Not what yo might expect

 

 

Very confusing hmmm..., here is the solution (add a temp variable):

foreach (char vowel in "aeiou")

{

   char temp = vowel;

   query = query.Where (c => c != temp);

}

 

Reason:

"The temporary variable in the loop is required to avoid the outer variable trap, where the same variable is captured for each iteration of the foreach loop." -- C# 3.0 IN A NUTSHELL http://www.albahari.com/nutshell/predicatebuilder.html

 

I would like a more detail explanation of this.

I'm guessing that the variable vowel goes through some sort of reset each time the foreach loop is ran, so in the end you will be actually checking

query = query.Where (c => c != 'u');

query = query.Where (c => c != 'u');

query = query.Where (c => c != 'u');

query = query.Where (c => c != 'u');

query = query.Where (c => c != 'u');

 

instead of

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');

4 comments:

Joseph Albahari said...

Hi William

Do you have the book (C# 3.0 in a Nutshell)? The book includes accompanying text that explains how each of the examples work.

I'm just wondering whether you're
(a) confused with the book's explanation - or,
(b) wondering what book says!

Joseph Albahari
(Author)

William Andrus said...

I don't have the book -- yet. I look forward to reading it, when it my order comes in the mail. I thank you for leaving a comment.

Anonymous said...

Thanks a lot! I had exactly the same problem!

Airton said...

This happens because of deferred execution. LINQ delays the real execution of queries until they are needed.

By using one exemple of the book itself (chapter 8: LINQ Queries: Deferred Execution):


int[] numbers = { 1, 2 };

int factor = 10;
IEnumerable<int> query = numbers.Select (n => n * factor);

factor = 20;

(...and then you show the results in any way)

Will result:

20
40

The lambda expression have its execution delayed until it is needed (when you show the data), so the value it will use for factor is 20, so both numbers are multiplied by 20, not 10.

Airton da Fonseca Granero