yield和LINQ之间的相互作用

时间:2011-08-05 22:02:10

标签: c# linq linq-to-xml yield

我正在阅读“XStreamingReader”库中的一段代码(这对于能够在XML文档上执行LINQ查询而不将实际文档加载到内存中(如在XDocument对象中)似乎是一个非常酷的解决方案 并且想知道以下事项:

public IEnumerable<XElement> Elements()
{
    using (var reader = readerFactory())
    {
        reader.MoveToContent();
        MoveToNextElement(reader);
        while (!reader.EOF)
        {
            yield return XElement.Load(reader.ReadSubtree());
            MoveToNextFollowing(reader);
        }
    }
}

public IEnumerable<XElement> Elements(XName name)
{
    return Elements().Where(x => x.Name == name);
}

关于第二种方法Elements(XName) - 该方法首先调用Elements(),然后使用Where()来过滤它的结果,但是我对这里的执行顺序很感兴趣()包含一个yield语句。 据我所知: - Executing Elements()返回一个IEnumerable集合,此集合实际上不包含任何项目YET。 - 在该集合上执行Where(),在场景后面有一个遍历每个项目的循环,因为正在使用yield,所以新项目在运行中被“加载”。 - 与Where语句匹配的所有项目都作为IEnumerable集合返回,并且在该集合中是物理的。

首先,我是否正确上述假设? 第二,如果我是对的 - 如果我想返回一个“已屈服”的集合而不是返回一个用所有过滤数据填充的集合,该怎么办? 我问这个是因为它失去了整个目的,即不将整个“匹配”块读入内存,而是一次迭代一个匹配元素......

3 个答案:

答案 0 :(得分:2)

我假设当你说物品在物品集合中时,你的意思是内存中有一个结构,其中包含现在的所有物品。对于Where(),情况并非如此,它内部使用yield(或者与yield的行为相同)。

当您尝试获取第一个项目时,Where()会迭代源集合,直到找到匹配的第一个项目。因此,元素在Elements()Elements(XName)中流式传输,整个集合永远不会在内存中,只是一片一片。

答案 1 :(得分:1)

  

在该集合上执行Where()   首先,我是否正确上述假设?

没有。哪里返回一个懒惰的IEnumerable<XElement>。稍后,当枚举IEnumerable<XElement>时,将生成并过滤元素。

如果枚举懒惰IEnumerable的东西恰好收集元素(例如对ToList的调用),那么所有元素都将在内存中。如果枚举惰性IEnumerable的事物恰好一次处理一个项目(例如foreach循环,它不保留对XElement的引用),那么一次只有一个项目将存在于内存中。

答案 2 :(得分:0)

  

与Where语句匹配的所有项目都作为IEnumerable集合返回,并且在该集合中是物理的。首先,我是否正确上述假设?

没有。 Where在内部实现了一个额外的枚举器,它可以执行您希望它执行的操作。如果未枚举IEnumerable,则永远不会调用读者,并且永远不会创建单个XElement实例,并且永远不会运行过滤代码。

请参阅Jon Skeet关于重新实现Where子句行为的文章:http://msmvps.com/blogs/jon_skeet/archive/2010/09/03/reimplementing-linq-to-objects-part-2-quot-where-quot.aspx。他模仿现有的实现(出于解释目的 - 不需要在实际代码中使用他的重新实现),并且他的代码使用yield return

请注意,如果您调用ToList,则会评估整个枚举并将其复制到列表中,因此请小心 IEnumerable Where返回。

还要记住,如果readerFactory返回的阅读器正在从内存中读取(例如StringReader),那么文档将在物理上存在于内存中 - 不会有任何DOM实例节点直到你枚举它们。一旦枚举了这些元素,您的文档将在内存中存在两次,一个用于原始文档,一个用于DOM格式。您可能希望确保您的流式传输是针对非内存流完成的(例如,直接来自文件或网络流)。