2008-08-04 60 views
32

我從來沒有完全滿意異常處理的工作方式,有很多異常和try/catch帶來的表(堆棧展開等),但它似乎打破了很多OO模型的過程。在C#中減少重複的錯誤處理代碼?

不管怎麼說,這就是問題所在:

假設你有一些類包裝或包括網絡文件IO操作(例如閱讀和寫作的地方在某些特定的UNC路徑某些文件)。出於各種原因,您不希望這些IO操作失敗,因此如果檢測到它們失敗,請重試它們,並且不斷重試它們,直到它們成功或達到超時。我已經有一個方便的RetryTimer類,我可以實例化並用它在重試之間休眠當前線程,並確定何時超時時間已過,等等。

問題是,您有一堆IO操作這個類,你需要在try-catch/retry邏輯中包裝它們。

下面是一個示例代碼片段:

RetryTimer fileIORetryTimer = new RetryTimer(TimeSpan.FromHours(10)); 
bool success = false; 
while (!success) 
{ 
    try 
    { 
     // do some file IO which may succeed or fail 
     success = true; 
    } 
    catch (IOException e) 
    { 
     if (fileIORetryTimer.HasExceededRetryTimeout) 
     { 
      throw e; 
     } 
     fileIORetryTimer.SleepUntilNextRetry(); 
    } 
} 

那麼,你如何避免整個類最複製該代碼的每個文件IO操作?我的解決方案是在執行傳遞給它的委託塊的類中使用匿名委託塊和單個方法。這讓我做這樣的事情在其他的方法:

this.RetryFileIO(delegate() 
    { 
     // some code block 
    }); 

我喜歡這個有點,但它留下來了很多有待改進。我想聽聽其他人會如何解決這類問題。

+1

只是一個通用的供參考:它是[幾乎*總是*更好](http://philosopherdeveloper.wordpress.com/2010/05/05/re-throwing-caught-exceptions/)只是`拋出;而是`throw e;` – 2010-09-13 03:34:47

回答

13

這看起來像一個極好的機會來看看面向方面編程。這是一篇關於AOP in .NET的好文章。總的想法是,你需要將交叉功能關注點(即x小時的重試)提取到一個單獨的類中,然後註釋任何需要以這種方式修改其行爲的方法。下面是它看起來可能會(對的Int32一個不錯的擴展方法)

[RetryFor(10.Hours())] 
public void DeleteArchive() 
{ 
    //.. code to just delete the archive 
} 
4

只是想知道,你覺得你的方法留下了什麼?你可以用..命名替換匿名代理?代表,像

public delegate void IoOperation(params string[] parameters); 

    public void FileDeleteOperation(params string[] fileName) 
    { 
     File.Delete(fileName[0]); 
    } 

    public void FileCopyOperation(params string[] fileNames) 
    { 
     File.Copy(fileNames[0], fileNames[1]); 
    } 

    public void RetryFileIO(IoOperation operation, params string[] parameters) 
    { 
     RetryTimer fileIORetryTimer = new RetryTimer(TimeSpan.FromHours(10)); 
     bool success = false; 
     while (!success) 
     { 
      try 
      { 
       operation(parameters); 
       success = true; 
      } 
      catch (IOException e) 
      { 
       if (fileIORetryTimer.HasExceededRetryTimeout) 
       { 
        throw; 
       } 
       fileIORetryTimer.SleepUntilNextRetry(); 
      } 
     } 
    } 

    public void Foo() 
    { 
     this.RetryFileIO(FileDeleteOperation, "L:\file.to.delete"); 
     this.RetryFileIO(FileCopyOperation, "L:\file.to.copy.source", "L:\file.to.copy.destination"); 
    } 
2

你也可以使用一個更面向對象的方法:

  • 創建一個基類,做了錯誤處理和調用一個抽象方法去執行具體的工作。 (模板方法模式)
  • 爲每個操作創建具體類。

這樣做的好處是可以命名您執行的每種類型的操作,併爲您提供命令模式 - 操作已被表示爲對象。

2

這是我最近做的。它在其他地方可能已經做得更好了,但它看起來很乾淨且可重用。

我有一個看起來像這樣的實用方法:

public delegate void WorkMethod(); 

    static public void DoAndRetry(WorkMethod wm, int maxRetries) 
    { 
     int curRetries = 0; 
     do 
     { 
      try 
      { 
       wm.Invoke(); 
       return; 
      } 
      catch (Exception e) 
      { 
       curRetries++; 
       if (curRetries > maxRetries) 
       { 
        throw new Exception("Maximum retries reached", e); 
       } 
      } 
     } while (true); 
    } 

然後在我的應用程序,我使用C#的的Lamda表達式語法保持整潔:

Utility.DoAndRetry(() => ie.GoTo(url), 5); 

這就要求我的方法和重試最多5次。在第五次嘗試中,原始異常在重試異常中重新生成。

+0

但爲什麼自定義`WorkMethod`委託而不是`Action`? – 2010-09-13 03:32:26