-->

产量回归待摊迭代问题(Yield return deferred iteration problem

2019-09-30 06:33发布

我知道,回报收益率采取延迟加载的优势,但我想知道如果我可能会被滥用或迭代器很可能需要重构。

我的递归迭代方法返回一个给定的所有祖先PageNode包括pageNode本身。

public class PageNodeIterator {
    //properties and constructor left out for brevity

    public IEnumerable<IPageNode> ancestorsOf(IPageNode pageNode) {
        if(pageNode == null) throw new ArgumentNullException(("pageNode"));

        if (pageNode.url != pageNodeService.rootUrl) {
            yield return pageNode;
            if (pageNode.parent != null)
                foreach (var node in ancestorsOf(pageNode.parent))
                    yield return node;
        }
    }
}

在我的呼吁ancestorsOf ,我调用该方法,然后反向返回的顺序IEnumerable ,但由于装载延迟呼叫实际上并没有发生,直到我打电话ToArray()的下一行,并在该点pageNodeService在我的iterator方法是空和空引用异常被抛出。

ancestors = pageNodeIterator.ancestorsOf(currentNode).Reverse();
return ancestors.ToArray()[1].parent.children;

所以,我想知道我已经错了。 什么是在这种情况下使用迭代器的正确方法,如果在所有?

我也想知道为什么pageNodeService是在执行的时间空。 即使是延迟执行不应该仍持有价值?

Answer 1:

不是一个真正的答案...更多建议的一种替代实现消除递归。 太长张贴注释。

    public IEnumerable<IPageNode> ancestorsOf(IPageNode pageNode) {
        if(pageNode == null) throw new ArgumentNullException(("pageNode"));
        Stack<IPageNode> stack = new Stack<IPageNode>();
        stack.Push(pageNode);
        while(stack.Any())
        {
            IPageNode n=stack.Pop();
            if (n.url != pageNodeService.rootUrl) {
                yield return n;
                if(n.parent != null)
                {
                    stack.Push(n.parent);
                }
            }
        }
    }

关于它的思考,你可以完全消除堆栈:

public IEnumerable<IPageNode> ancestorsOf(IPageNode pageNode) {
    if(pageNode == null) throw new ArgumentNullException(("pageNode"));
    IPageNode n = pageNode;
    while(n != null && n.url != pageNodeService.rootUrl)
    {
        yield return n;
        n = n.parent;
    }
}


Answer 2:

我不知道你的错误是,StackOverflow的和没有调试代码的服务; 我会在调试器中运行它,并寻找错误解决您的问题。

不过,我会借此机会指出的是这样的:

public IEnumerable<IPageNode> AncestorsOf(IPageNode pageNode) {
    if(pageNode == null) throw new ArgumentNullException(("pageNode"));
    // Do stuff that yields 

稍微有问题的,因为没有在块中的代码的运行,直到MoveNext被称为首次。 换句话说,如果你这样做:

var seq = AncestorsOf(null); // Not thrown here!
using (var enumtor = seq.GetEnumerator())
{
    bool more = enumtor.MoveNext(); // Exception is thrown here!

这是非常令人吃惊的人。 相反,写你这样的代码:

public IEnumerable<IPageNode> AncestorsOf(IPageNode pageNode) {
    if(pageNode == null) throw new ArgumentNullException(("pageNode"));
    return AncestorsOfIterator(pageNode);
}
private IEnumerable<IPageNode> AncestorsOfIterator(IPageNode pageNode)
{
    Debug.Assert(pageNode != null);
    // Do stuff that yields 
}


Answer 3:

它甚至意义在这个地方使用收益-通过调用反向以来, 所有的东西无论如何必须缓冲 ,所以你可以改为只返回祖先的完整列表。



Answer 4:

添加起始节点这个迭代器外,如果你需要它。

public class PageNodeIterator {
    //properties and constructor left out for brevity

    public IEnumerable<IPageNode> ancestorsOf(IPageNode pageNode) {
        if(pageNode == null) throw new ArgumentNullException(("pageNode"));

        if (pageNode.url != pageNodeService.rootUrl)
        {
            if (pageNode.parent != null ) 
            {
                yield return pageNode.parent;
                yield return ancestorsOf(pageNode.parent);
            }
        }
    }
}


文章来源: Yield return deferred iteration problems