2008-08-04 13 views
32

私は例外処理の仕方に完全に満足していることは一度もありません。多くの例外があり、try/catchはテーブルに持ち込みます(スタックの巻き戻しなど)が、プロセス内の多くのオブジェクト指向モデルを破壊するようです。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

ちょうど一般的なFYI:単純に「投げる」というだけでは(ほとんどの場合、より良い)(http://philosopherdeveloper.wordpress.com/2010/05/05/re-throwing-caught-exceptions/)です –

答えて

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#のラムダ式の構文を使用します。

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

これは私の方法と再試行を呼び出します最大5回。 5回目の試行では、元の例外が再試行例外の内部で再投入されます。

+0

しかし、なぜ 'Action'の代わりにカスタム' WorkMethod'デリゲートを使うのでしょうか? –