【问题标题】:StackOverflowException caused by Recursion递归引起的 StackOverflowException
【发布时间】:2013-03-02 13:27:06
【问题描述】:

我目前正在编写一个程序来帮助编写 Lore。每个书本对象都可以是父母并有孩子。这意味着每个孩子都可以有孩子等等。我正在研究一个 ToString() 方法,该方法可以使用递归来解决这个问题,但我不断收到 StackOverflowException。

我知道这意味着什么,但我不确定如何解决它。我是 C# 的新手,但有很多 Java 经验,所以如果你知道一个技巧或我错过的东西,请告诉我!

所以我的问题是:如何避免 StackOverflow 异常?问题出在 GetAllChildren()

编辑:

运行测试后,我应该得到这样的结果:

Name: a
Children:
b
c
    d
e

使用来自@lc 的代码。我得到以下输出:

Name: a
Children: No Children   b
c
e
    b
c
e
    b
c
e

这是课程:

class Book
{
    private String name;
    private Book[] children;
    private StringBuilder text;
    private Boolean isParent;

    public Book(String name, Book[] children, StringBuilder text, Boolean isParent)
    {
        this.name = name;
        this.children = children;
        this.text = text;
        this.isParent = isParent;
    }

    /**
     * Most likely all possible Constructors
     * */
    public Book(String name, Book[] children) : this(name, children, new StringBuilder("No Text"), true) { }
    public Book(String name, String text) : this(name, new Book[0], new StringBuilder(text), false) { }
    public Book(String name, StringBuilder text) : this(name, new Book[0], text, false) { }
    public Book(String name) : this(name, new Book[0], new StringBuilder("No Text"), false) { }
    public Book(Book[] children, String text) : this("Unnamed Book", children, new StringBuilder(text), true) { }
    public Book(Book[] children, StringBuilder text) : this("Unnamed Book", children, text, true) { }
    public Book(Book[] children) : this("Unnamed Book", children, new StringBuilder("No Text"), true) { }
    public Book(StringBuilder text) : this("Unnamed Book", new Book[0], text, false) { }
    public Book() : this("Unnamed Book", new Book[0], new StringBuilder("No Text"), false) { }

    public String Name
    {
        get { return name; }
        set { name = value; }
    }

    public Book[] Children
    {
        get { return children; }
        set { children = value; }
    }

    /**
     * Will Return the StringBuilder Object of this Text
     * */

    public StringBuilder Text
    {
        get { return text; }
        set { text = value; }
    }

    public Boolean IsParent
    {
        get { return isParent; }
        set { isParent = value; }
    }

    private void GetAllChildren(Book book, StringBuilder sb)
    {
        if (book.isParent)
        {
            GetAllChildren(book, sb);
        }
        else
        {
            sb.Append("\t");
            foreach (Book b in children)
            {
                sb.Append(b.Name + "\n");
            }
        }
    }

    public override String ToString()
    {
        StringBuilder sChildren = new StringBuilder("No Children");
        if (children.Length != 0)
        {
            GetAllChildren(this, sChildren);
        }

        return "Name: " + name + "\n" +
            "Children: " + sChildren.ToString();
    }
}

【问题讨论】:

  • 通过stackoverflow.com询问
  • @SiGanteng 我正在考虑开这个玩笑,但我没有选择太多;P
  • isParent 分支中调用GetAllChildren(book, sb); 背后的想法是什么?请注意,booksb 均未更改。
  • @Damien_The_Unbeliever 不确定我是否理解您的问题。

标签: c# recursion stack-overflow


【解决方案1】:

我想你的意思是:

if (book.isParent)
{
    foreach (var child in book.Children)
        GetAllChildren(child, sb);
}

否则,您只是一遍又一遍地使用相同的参数 (book, sb) 调用 GetAllChildren 方法。


旁注-您仍然有一些问题,因为GetAllChildren 中的停止条件正在遍历子代,而它不应该(如果它不是父代,则它不应该有子代)。相反,它应该返回自己的名称。此外,每个孩子还应该在上面的 foreach 循环中附加自己的名字(或者实际上,每本书都应该附加自己的名字)。

旁注 2 - 所写的方法(带有这些更改)应该是静态的,因为它与任何给定的实例都不相关(这让我想到了下面的建议)。


建议 - 我会推荐类似以下的内容(未经测试,需要在格式化方面做一些工作):

//name changed to reflect what it really does
//also changed to be an instance method (we no longer pass in a Book)
//added listThisBooksName parameter to allow supressing the topmost book's output
private void AppendAllChildren(StringBuilder sb, int level = 0, 
    bool listThisBooksName = false)
{
    if (listThisBooksName)
    {
        //append ourself here

        //first indent however far we need to
        sb.Append(new String('\t', level));

        //now add our name
        sb.Append(this.Name);

        //and a newline (you can strip the last one later if you want)
        sb.Append('\n');
    }

    //forget the "isParent" property, just check if it has any children
    //we don't need Children.Any() because the foreach will just iterate 0 times
    //you might also consider using a List<Book> instead of an array for Children
    if (this.Children != null)
        foreach (var child in this.Children)
            child.AppendAllChildren(sb, level+1, true);
}

【讨论】:

  • 我从第一个注释中尝试了您的代码,而不是您的静态代码,并且不再收到异常,但请查看我的编辑。该方法仍然关闭。
  • 几乎完美。父级将自己列为子级。
  • @Vipar 不知道您想列出与孩子分开的最顶层父母。检查我的编辑。我假设你明白这段代码在做什么?
  • 我们去!完美的!非常感谢 :) 对于任何未来的读者,静态方法都有效!
【解决方案2】:

当该书的 IsParent 为真时,您的递归是在同一本书上递归。如果这本书是父母,您可能希望递归所有孩子。

【讨论】:

    【解决方案3】:

    这不是问题吗:

        if (book.isParent)
        {
            GetAllChildren(book, sb);
        }
    

    然后你又调用了同样的方法?我认为以上内容应该遍历孩子并为每个孩子Book 调用GetAllChildren。仅当您的Book 没有有任何孩子时才输出名称。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-10
      • 2019-02-07
      • 2013-07-11
      • 2016-09-19
      • 2013-09-06
      • 2010-09-19
      • 2010-10-21
      • 1970-01-01
      相关资源
      最近更新 更多