IEnumerable
is a query that is lazily evaluated. But apparently my understanding is a bit flawed. I'd expect the following to work:
// e.Result is JSON from a server
JObject data = JObject.Parse(e.Result);
JsonSerializer serializer = new JsonSerializer();
// LINQ query to transform the JSON into Story objects
var stories = data["nodes"].Select(
obj => obj["node"]).Select(
storyData => storyOfJson(serializer, storyData));
// set a value on each story returned by the query
foreach (Story story in stories)
{
story.Vid = vid;
}
// run through the query again, making sure the value was actually set
foreach (Story story in stories)
{
// FAILS - story.VID is 0
Debug.Assert(story.Vid == vid);
}
What am I misunderstanding here? How can I alter the results of what this query returns?
Each time you enumerate the stories
variable, the Select
call runs again, creating a new set of Story
objects.
Therefore, each foreach
loop runs on a different set of Story
instances.
You need to force the LINQ calls to evaluate exactly once by calling .ToArray()
.
Looping through the resulting array will not re-evaluate the LINQ calls (since it's a normal array), so you'll share the same set of Story
instances.
The IEnumerable
in this case contains the return values of storyOfJson
called repeatedly on the collection, not the original values.
When you enumerate the collection inside the foreach
, the function is repeatedly called as necessary, and the result of the function call is placed into the foreach
iteration variable (story
in this case).
If you want to store the set of Story
objects, you'll have to put them into some form of collection first, because the enumerated items are destroyed once the loop terminates.
Side Note: You should probably use the LINQ syntax rather than the functional syntax when possible.
var stories = data["nodes"].Select(obj => obj["node"]).Select(storyData => storyOfJson(serializer, storyData));
becomes
var stories = from node in data["nodes"]
select storyOfJson(serializer, node["node"]);
IEnumerable
is supposed to be treated like a read-only collection. While some programs might return an existing collection, rather than a copy, other programs are free to return a new copy each time. For example, when you add .Select(storyData => storyOfJson(...))
, you will get new objects each time.
This means that if you edit the copy you get, then perform the query/call get again, then your changes will be wiped out.
If you want to edit the results of the query, either add your own .Select()
on top of the resulting query, to perform the transformation you are interested in, ala:
var filteredStoried = stories.Select(s => new Story(s) { Vid = vid });
foreach(var s in filteredStories)
Assert(s.Vid == vid);
Or copy the query results to a local collection, and work with that:
var localStories = new List<Story>(stories);
foreach(var s in localStories)
s.Vid = vid;
foreach(var s in localStories)
Assert(s.Vid == vid);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With