40

例外処理の仕組みに完全に満足したことはありません。多くの例外があり、try / catchがテーブルにもたらします(スタックの巻き戻しなど)が、その過程で多くのOOモデルが壊れているようです。

とにかく、ここに問題があります:

ネットワーク化されたファイル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
    } );

私はこれがやや好きですが、それはまだまだ望まれていません。他の人がこのような問題をどのように解決するのか聞きたいです。

4

4 に答える 4

14

これは、アスペクト指向プログラミングを検討する絶好の機会のようです。.NET での AOPに関する優れた記事を次に示します。一般的な考え方は、機能横断的な問題 (x 時間の再試行) を別のクラスに抽出し、そのように動作を変更する必要があるメソッドに注釈を付けるというものです。これがどのように見えるかです (Int32 の優れた拡張メソッドを使用)

[RetryFor( 10.Hours() )]
public void DeleteArchive()
{
  //.. code to just delete the archive
}
于 2008-08-05T09:43:55.000 に答える
4

ただ疑問に思っているのですが、あなたの方法は何が望まれていると思いますか?匿名のデリゲートを..namedに置き換えることができますか?デリゲート、

    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" );
    }
于 2008-08-04T20:07:41.840 に答える
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回再試行します。5回目の試行で、元の例外が再試行例外内で再スローされます。

于 2010-09-13T02:25:02.620 に答える
2

よりオブジェクト指向のアプローチを使用することもできます。

  • エラー処理を行い、抽象メソッドを呼び出して具体的な作業を実行する基本クラスを作成します。(テンプレートメソッドのパターン)
  • 操作ごとに具体的なクラスを作成します。

これには、実行する操作の各タイプに名前を付けるという利点があり、コマンド パターンを提供します。操作はオブジェクトとして表されています。

于 2008-08-07T11:30:38.853 に答える