2

例外をスローする可能性のあるデリゲートをBackgroundWorker.Errorいつ呼び出さなければならないかをどのように意味のあるものにしますか?DoWork

カスタムメッセージをユーザーに伝えるさまざまな方法を公開する静的MsgBoxクラスを実装しています (カスタムメッセージフォームを使用)。

公開されたメソッドの 1 つである は、 (カスタム から派生した) をShowProgressインスタンス化し、何らかのバックグラウンド操作が行われている間、進行状況バーを表示します (ユーザーはその間ずっと操作をキャンセルできます)。モーダル フォームでバックグラウンド タスクを実行するのがぎこちなく聞こえる場合は、次のような署名を検討してください。ProgressMessageBoxFormMessageBoxForm

public static DialogResult ShowProgress(string title, string message, _
    Action<AsyncProgressArgs> progressAction)

アイデアは、その「作業」を任意の提供されたメソッド (/handler) に委譲するバックグラウンド ワーカーをカプセル化し、そのメソッドが「対話」して進行状況とキャンセルを報告できるようにすることです...そして理想的にはエラー ステータスです。フォームがモーダルであるかどうかは、このコンテキストでは関連性がありません。キャンセル可能なモーダル フォームの進行状況バーに実行中のタスクの進行状況を表示する機能は、チェックボックスのチェックを外さない限り自動的に閉じ、表示されたまま表示されます。完了時の成功ステータスは必須機能です。

問題のAsyncProgressArgsクラスは次のとおりです。

public class AsyncProgressArgs
{
    private readonly Action<int, string> _update;
    private readonly BackgroundWorker _worker;
    private readonly DoWorkEventArgs _workEventArgs;

    public AsyncProgressArgs(Action<int, string> updateProgress, BackgroundWorker worker, DoWorkEventArgs e)
    {
        _update = updateProgress;
        _worker = worker;
        _workEventArgs = e;
    }

    /// <summary>
    /// Reports progress to underlying <see cref="BackgroundWorker"/>.
    /// Increments <see cref="ProgressBar"/> value by specified <see cref="int"/> amount and
    /// updates the progress <see cref="Label"/> with specified <see cref="string"/> caption.
    /// </summary>
    public Action<int, string> UpdateProgress { get { return _update; } }

    /// <summary>
    /// Verifies whether asynchronous action is pending cancellation,
    /// in which case asynchronous operation gets cancelled.
    /// </summary>
    public void CheckCancelled()
    {
        _workEventArgs.Cancel = _worker.CancellationPending;
    }
}

のイベント ハンドラは、カスタム メッセージボックスに渡されたメソッドを呼び出しますBackgroundWorkerDoWork

protected virtual void worker_DoWork(object sender, DoWorkEventArgs e)
{
    // *** If RunWorkerCompletedEventArgs.Error caught exceptions,
    //     then this try/catch block wouldn't be needed:
    // try
    // {
           _startHandler(this, new AsyncProgressArgs(UpdateProgressAsync, _worker, e));
    // }
    // catch(Exception exception)
    // {
    //     if (MsgBox.Show(exception) == DialogResult.Retry)
    //     {
    //         BeginWork(_startHandler);
    //     }
    //     else
    //     {
    //         Hide();
    //     }
    // }
}

このような方法が与えられた場合:

private void AsyncProgressAction(AsyncProgressArgs e)
{
    // do some work:
    Thread.Sleep(200);

    // increment progress bar value and change status message:
    e.UpdateProgress(10, "Operation #1 completed.");

    // see if cancellation was requested:
    e.CheckCancelled();


    // do some work:
    Thread.Sleep(500);

    // increment progress bar value and change status message:
    e.UpdateProgress(30, "Operation #2 completed.");

    // see if cancellation was requested:
    e.CheckCancelled();

    // ...

    // throw new Exception("This should be caught by the BackgroundWorker");
}

呼び出しコードは次のようになります。

MsgBox.ShowProgress("Async Test", "Please wait while operation completes.", _
    AsyncProgressAction);

アクションメソッドで例外がスローされるまで、すべてが期待どおりに機能します (プログレスバーが移動し、プロセスをキャンセルできます)。通常、BackgroundWorkerはそれをキャッチしてそのプロパティに格納しErrorますが、ここではそうはなりません。

したがって、渡されたアクション メソッド内のコードは独自の例外を処理する必要があり、そうでない場合は処理されないままになり、プログラムは悲惨な死を遂げます。

問題は、そのような構造を持つことが可能でありError、プロセスが完了したときに意味のあるプロパティを持つことができるかどうかです。Throw new Exception()アクションメソッドのどこでもできるようにして、カプセル化されたワーカーで処理したいと考えています。

補足として、すべての作業が完了するまでプログレスバーを動かすことができず、オブジェクトインスタンスを直接処理したくないBackgroundWorkerので、私は頼りました。TaskThread

編集 これは問題ではありません。コンパイルされたアプリは爆発しません。実際、デリゲート メソッドでスローされた例外でデバッガーが中断することに混乱しました。以下のコメントで指摘されているように、実行/デバッグはその後も続行でき、実行する予定のエラー処理ロジックはすべて実行されます。BackgroundWorkerどういうわけか例外とデバッガーが続行することを期待していcatchましたが、例外がキャッチされ、それでもデバッガーが壊れていることがわかりました。

前にこれを読むべきだった: BackgroundWorker で未処理の例外

4

1 に答える 1

1

前にこれを読んでおくべきでした: BackgroundWorker の未処理の例外

これは非常に長い質問であり、単純な非問題のコンテキストが多数あります。VS デバッガーが停止し、未処理の例外の場合と同様に、誤解を招くように「例外はユーザー コードによって処理されませんでした」と言います...実際には「BackgroundWorker」を意味する場合タスクが例外をスローし、デバッガーがそれを通知しています。そうしないと、BackgroundWorker が例外を飲み込んでいると思われるからです。」

例外は F5 キーを押したり無視したりして実行を再開できます。例外は予期したとおりに終了RunWorkerCompletedEventArgs.Error、デプロイされたアプリはユーザーの目の前で爆破されません。

この回答を投稿して、未回答の質問のスタックからこの質問を削除します...

于 2013-02-14T01:11:11.403 に答える