Changes to an IEnumerable are not being kept between queries

限于喜欢 提交于 2019-12-23 09:02:22

问题


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?


回答1:


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.




回答2:


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"]);



回答3:


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


来源:https://stackoverflow.com/questions/4083771/changes-to-an-ienumerable-are-not-being-kept-between-queries

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!