【问题标题】:IEnumerable implementation breaks on foreachIEnumerable 实现在 foreach 上中断
【发布时间】:2014-02-25 19:08:28
【问题描述】:

我使用 PDFNet 库从 PDF 中提取对象,然后是 OCR。我实例化我的Elements 对象:

public class Processor
{
    public static int Main(string[] args)
    {
       Elements pdfPageElements = new Elements(pdfPage);
       ...

构造函数(在一个单独的类中)看起来像

internal class Elements : IEnumerator<Element>, IEnumerable<Element>
{
    private readonly int _position;
    private readonly ElementReader _pdfElements;
    private Element _current;

    public Elements(Page currentPage)
    {
        _pdfElements = new ElementReader();
        _pdfElements.Begin(currentPage);
        _position = 0;
    }

    ...

在实例化 pdfPageElements 之后,我返回 Main() 并使用 Linq 遍历集合项以获取我想要的 PDF 对象(在本例中为图像)。

var pdfPageImages = (from e in pdfPageElements
                     where
                         (e.GetType() == Element.Type.e_inline_image ||
                          e.GetType() == Element.Type.e_image)
                     select e);

PDFNet SDK 实现 MoveNext() 方法如下:

public bool MoveNext()
{
   if ((_current = _pdfElements.Next()) != null)
    {
        return true;
    }
    else
    {
        _pdfElements.Dispose();
         return false;
     }
 }

pdfPageImages 被很好地实例化; Console.WriteLine(pdfPageImages.Count()); 为我的测试 PDF 返回正确数量的图像。

但是当我通过foreach loop 发送pdfPageImages 时,我得到以下异常:

pdftron.Common.PDFNetException: Unknown exception.
 at pdftron.PDF.ElementReader.Next()
 at pdftron.Elements.MoveNext()
 at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
 at DM_PDFProcessor.Processor.Main(String[] args)

可能值得注意的是,在 PDFNet 文档中它指出:

Every call to ElementReader::Next() destroys the current Element. 
Therefore, an Element becomes invalid after subsequent 
ElementReader::Next() operation.

但是,一旦元素被读入 IEnumerable pdfPageImages,它应该是无限期可迭代的(根据我有限的理解)。


请注意,集合中的元素肯定 不为空。任何想法为什么我不断收到异常?

【问题讨论】:

  • 它实现了IEnumerable&lt;T&gt; IEnumerator&lt;T&gt;这一事实几乎总是一个不好的迹象,除非它是使用迭代器块实现的(在这种情况下编译器会这样做正确的事情)。听起来Element 类被设计破坏了。
  • 如果Count() 方法迭代集合(如果没有Count 属性或类似属性,它将执行此操作),那么ElementReader::Next 方法会在每个元素被访问后销毁它。所以下次你尝试迭代集合时,元素不再有效。

标签: c# linq foreach enumeration


【解决方案1】:

注意

var pdfPageImages = (from e in pdfPageElements
                     where
                         (e.GetType() == Element.Type.e_inline_image ||
                          e.GetType() == Element.Type.e_image)
                     select e);

被懒惰地评估。也就是说,每次枚举pdfPageImages时,也会枚举pdfPageElements。所以如果Elements类被构建为一个实例只能枚举一次而不抛出,你可能想要缓存查询结果:

var pdfPageImages = (from e in pdfPageElements
                     where
                         (e.GetType() == Element.Type.e_inline_image ||
                          e.GetType() == Element.Type.e_image)
                     select e).ToList();

【讨论】:

    猜你喜欢
    • 2010-12-28
    • 1970-01-01
    • 1970-01-01
    • 2014-12-04
    • 1970-01-01
    • 2010-09-12
    • 1970-01-01
    • 2015-03-12
    • 2013-09-24
    相关资源
    最近更新 更多