2013-03-14 78 views
7

我目前正在編寫一個程序來幫助寫Lore。每個書對象都可以是父母並且有孩子。這意味着每個孩子都可以有孩子等無限。我正在處理一個ToString()方法,它可以使用遞歸來解釋這個問題,但我一直在收到一個StackOverflowException。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(); 
    } 
} 
+6

通過在'stackoverflow.com查詢 – 2013-03-14 09:15:37

+1

@SiGanteng我正在考慮做笑話,但我不選擇; P – OmniOwl 2013-03-14 09:16:29

+0

在'isParent'分支內調用'GetAllChildren(book,sb)'後面的想法是什麼? ?請注意,'book'和'sb'都沒有被改變。 – 2013-03-14 09:19:03

回答

8

我想你的意思是:

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

否則你只是調用GetAllChildren方法具有相同的參數(book, sb)一遍又一遍。


附註 - 你仍然有一些問題,因爲在GetAllChildren停止條件是通過迭代的孩子,當它不應該(如果不是家長,它不應該有孩子)。它應該返回它自己的名字。此外,每個孩子還應該在上面的foreach循環中追加它的名字(或者實際上,每本書都應該附加自己的名字)。 (注意這些變化)的方法應該是靜態的,因爲它與任何給定的實例都不相關(這引出了下面的建議)。


建議 - 我建議像下面代替(未經測試,需要在格式化一些工作):

//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); 
} 
+0

我從第一個註釋中嘗試了您的代碼,而不是您的靜態代碼,並停止了獲取異常,但請查看我的編輯。該方法仍然關閉。 – OmniOwl 2013-03-14 09:27:19

+0

@Vipar你看過我的最新編輯嗎? (剛纔) – 2013-03-14 09:35:28

+0

幾乎完美。父列表本身雖然是小孩。 – OmniOwl 2013-03-14 09:37:40

2

這不是問題:

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

你再次調用相同的方法?我認爲上面應該遍歷孩子,併爲每個孩子Book致電GetAllChildren。只有輸出名稱如果您的Book有任何孩子。

2

您的遞歸在同一本書上遞歸,而該書的IsParent爲true。如果這本書是父母,你可能想要通過所有的孩子遞歸。