stand__sure (3) [Avatar] Offline
#1
There is a query nuance that warrants a note. The code in Chapter 3's QueryReuse project hides an important aspect: the source portion of the query expression is immediately dereferneced (i.e. if the array pointed to by the variable is changed, the query expression does NOT update). The following code demonstrates this:

int[] numbers = { 1, 2, 3, 4, 5 };
var query = from number in numbers
select Square(number);
numbers = Array.ConvertAll(numbers, number => number + 10);
foreach (var number in query)
Console.WriteLine(number);

When the for-each loop runs, the query will look at the values 1,2,3,4,5 and not 11,12,13,14,15. The reason for this is clear when you know it is going to happen, BUT it is not clear a priori. This warrants a note to the reader to highlight the subtlety.
fabrice.marguerie (224) [Avatar] Offline
#2
Re: Chapter 3 needs a note
Thanks for pointing this out. I'll add a note to the errata so that we consider adding a note in the next edition of the book.

Fabrice
stand__sure (3) [Avatar] Offline
#3
Re: Chapter 3 needs a note
Thanks.

One question comes to mind: short of re-assigning the query variable, is there a way to make the source parameter update?
fabrice.marguerie (224) [Avatar] Offline
#4
Re: Chapter 3 needs a note
Let's consider what happens when you write your query:
var query = from number in numbers select Square(number);

This code is equivalent to:
var query = Enumerable.Select(numbers, number => Square(number));

This is what it gets compiled into. As can be seen more clearly with this syntax, numbers is passed as a parameter to the Select method. Internally, Select creates an iterator that keeps a reference to numbers.

When you use Array.ConvertAll, your create a new instance/collection that is completely different than the one that was used in the query (and that is known by the iterator). This explains why the query still uses the previous collection. There is no way you can change the collection used by the query. The only solution is to change the content of the collection, as we do in the book sample.

Note that if your change numbers from an array to a List<int>, you can do whatever you want with the content of the list, including changing it completely, and the query will operate on the new content.
You can test the following, for example:

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var query = from number in numbers select Square(number);
numbers.Clear();
numbers.Add(10);
numbers.Add(20);
foreach (var number in query)
Console.WriteLine(number);
stand__sure (3) [Avatar] Offline
#5
Re: Chapter 3 needs a note
I might have been unclear.

Most would expect "reuse the query object" to mean that the actual object referenced by a token can be changed (and not just the constituent members of the object).

The following illustrates the same issue as the array case:

List<int> list1 = new List<int>();
list1.AddRange(new int[] { 1, 2, 3, 4, 5 });

Console.WriteLine("list1");
list1.ForEach(item => Console.WriteLine(item));

var query = from number in list1
select number.Square();

Console.WriteLine("Query results...");
query.ForEach(item => Console.WriteLine(item));


list1 = list1.ConvertAll(number => number + 10);

query.ForEach(item => Console.WriteLine(item));

Console.WriteLine("Updated list1");
list1.ForEach(item => Console.WriteLine(item));

Console.WriteLine("Query did NOT update...");
query.ForEach(item => Console.WriteLine(item));

// I was trying to avoid having to do this re-assignment
query = from number in list1
select number.Square();

Console.WriteLine("Query did update...");
query.ForEach(item => Console.WriteLine(item));

It is clear that the following is the case:
List<int> list2 = list1;
list1 = list1.ConvertAll(number => number + 10);
// list1 and list2 are no longer the same

But it is not clear when you are only using the token and not making an assignment -- hence, the request for the note. One would assume that you were defining something akin to a formula when writing a query (this is not the case).

My question previously was if it was possible to avoid redefining the query expression (i.e. if it was possible to make the symbol update to the new address). I understand that this is not possible... Nonetheless, I worry that a number of bugs are going to be introduced by unqualified statements about query reuse.