2011-05-18 91 views
0

給定一個名爲OrderInfo類的,什麼是保證其他開發人員(包括我自己)的最好辦法不小心讓這裏顯示的遞歸誤差:如何防止遞歸

public class OrderInfo : ICollection<UnitModule> { 

    // Other code for class... 

    public bool Changed { get; private set; } 

    public void Save() { 
    Save(this); // I want the static method to handle saving the data 
    } 

    public static void Save(OrderInfo item) { 
    for (int i = 0; i < item.Count; i++) { 
     if (item[i].Changed) { 
     item[i].Save(); 
     } 
    } 
    if (item.Changed) { 
     item.Save(); // this would be bad! 
     // Instead, all of the other developers should have to call the 
     // Database Save method. 
     // Is there a way to ensure this happens or do I have to rely on 
     // everyone remembering this? 
    } 
    } 

} 

編輯:使用明顯的答案,我可以寫我的類,如下所示(爲什麼並不重要 - 這只是防止遞歸):

public class OrderInfo : ICollection<UnitModule> { 
    // Other code for class... 
    bool saving; // <= new variable 
    public bool Changed { get; private set; } 

    public void Save() { 
    if (!saving) { 
     Save(this); 
    } else { 
     throw new Exception("This item is already being saved."); 
    } 
    } 

    public static void Save(OrderInfo item) { 
    item.saving = true; 
    try { 
     for (int i = 0; i < item.Count; i++) { 
     if (item[i].Changed) { 
      item[i].Save(); 
     } 
     } 
     if (item.Changed) { 
     // item.Save(); <= NOTE: this would throw an exception 
     DataAccess.Save(item); 
     item.Changed = false; 
     } 
    } finally { 
     item.saving = false; 
    } 
    } 
} 
+1

你有什麼特別的理由讓它成爲一個靜態成員? – Skizz 2011-05-18 21:58:05

+2

指向這裏的[答案](http://stackoverflow.com/questions/6051501/how-to-prevent-recursion)會是陳詞濫調嗎? – 2011-05-18 22:05:10

+0

@Marc Gravell - 即將發佈完全相同的評論! – 2011-05-18 22:07:01

回答

2

這可能會實現:

public static void Save(OrderInfo item) { 
    for (int i = 0; i < item.Count; i++) { 
    if (item[i].Changed) { 
     item[i].Save(); 
    } 
    } 
    if (item.Changed) { 
    item.Changed = false; // prevents recursion! 
    item.Save(); 
    if error saving then item.Changed = true; // reset if there's an error 
    } 
} 
+0

我得到了這個版本,實際上做我想要的。謝謝! – jp2code 2011-05-19 19:10:25

4

通常,當堆在超過極限的崩潰作爲一個警告,什麼是錯的。只要代碼在進入生產之前得到粗略的測試,有人會發現這個錯誤。

1

我會用下列方法之一按優先順序排列:

  • 重構代碼,以防止開發商錯誤
  • 應用靜態分析工具,作爲FxCop與一個相當具體的規則,檢查這種情況下或類似案件
  • 添加一種if-check的某種防止呼叫
  • 如果運行時性能不是問題,請查看堆棧幀以查明調用狀態,並至少引發異常。儘管這很脆弱和醜陋。
2

我不認爲你的問題是不需要的遞歸。我認爲你的問題是,你已經實現了一個靜態方法,它將類的一個實例作爲唯一的參數。

爲什麼你甚至首先創建了一個靜態方法?什麼是任何方法的使用案例與簽名public void Foo.Bar(Foo f)?爲什麼你會想要使用Foo.Bar(f)而不是f.Bar()?這個問題可能有很好的答案,我不知道。但那是我首先想到的。

+0

目標是讓Save類成員創建一個調用靜態方法的線程。傳遞給靜態方法的特定參數將需要更改爲通用對象,但這並不重要。 – jp2code 2011-05-19 14:04:39

+0

我必須承認,我仍然不明白你爲什麼要這樣做。 – 2011-05-19 17:57:59

1

這看起來就像是一種代碼味道(如果某件東西看起來像一種「味道」)。我會考慮重構這個類。爲什麼OrderInfo具有公共靜態方法,看起來可以用於保存其他OrderInfo實例?

0

可以做到這一點:

private bool saving; 
public void Save() 
{ 
    if(this.saving) 
    { 
     throw new InvalidOperationException("accidental recursion"); 
    } 
    try 
    { 
     this.saving = true; 
     OrderInfo.Save(this); // I want the static method to handle saving the data 
    } 
    finally 
    { 
     this.saving = false; 
    } 
} 

但正如其他人說你應該可能重構代碼是不容易出錯。