IEnumerable
是一个懒惰评估的查询。但显然我的理解有点瑕疵。我期待以下工作:
// 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);
}
我在这里误解了什么?如何更改此查询返回的结果?
答案 0 :(得分:11)
每次枚举stories
变量时,Select
调用都会再次运行,从而创建一组新的Story
个对象。
因此,每个foreach
循环都在不同的Story
个实例集上运行。
您需要强制LINQ调用通过调用.ToArray()
来评估一次
循环生成的数组不会重新评估LINQ调用(因为它是一个普通的数组),所以你将共享同一组Story
个实例。
答案 1 :(得分:1)
在这种情况下,IEnumerable
包含在集合上重复调用的storyOfJson
的返回值,而不是原始值。
当您在foreach
中枚举集合时,会根据需要重复调用该函数,并将函数调用的结果放入foreach
迭代变量(story
中情况)。
如果要存储Story
个对象的集合,首先必须将它们放入某种形式的集合中,因为一旦循环终止,枚举的项目就会被销毁。
备注:如果可能,您应该使用LINQ语法而不是函数语法。
var stories = data["nodes"].Select(obj => obj["node"]).Select(storyData => storyOfJson(serializer, storyData));
变为
var stories = from node in data["nodes"]
select storyOfJson(serializer, node["node"]);
答案 2 :(得分:1)
IEnumerable
应该被视为只读集合。虽然某些程序可能会返回现有的集合而不是副本,但其他程序每次都可以自由返回新的副本。例如,当您添加.Select(storyData => storyOfJson(...))
时,每次都会获得新对象。
这意味着如果你编辑你得到的副本,然后再次执行查询/调用get,那么你的更改将被删除。
如果要编辑查询结果,请在生成的查询之上添加自己的.Select()
,以执行您感兴趣的转换,ala:
var filteredStoried = stories.Select(s => new Story(s) { Vid = vid });
foreach(var s in filteredStories)
Assert(s.Vid == vid);
或者将查询结果复制到本地集合,并使用它:
var localStories = new List<Story>(stories);
foreach(var s in localStories)
s.Vid = vid;
foreach(var s in localStories)
Assert(s.Vid == vid);