2

我知道 yield return 利用延迟加载,但我想知道我是否可能滥用迭代器或很可能需要重构。

我的递归迭代器方法返回给定的所有祖先,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在我的迭代器方法中为 null 和抛出空引用异常。

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

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

我也想知道为什么pageNodeService在执行时为空。即使执行被推迟,它不应该仍然有价值吗?

4

4 回答 4

10

不知道你的bug在哪里,而且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 
}
于 2013-07-23T17:45:52.837 回答
3

并不是真正的答案……更多的是对消除递归的替代实现的建议。太长了,无法作为评论发布。

    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;
    }
}
于 2013-07-23T15:24:16.060 回答
2

在这个地方使用 yield 是否有意义 - 因为通过调用 Reverse,所有的东西都必须被缓冲,这样你就可以只返回完整的祖先列表。

于 2013-07-23T15:17:30.687 回答
0

如果需要,请在此迭代器之外添加起始节点。

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);
            }
        }
    }
}
于 2013-07-23T15:25:16.010 回答