例外をスローする可能性のあるデリゲートをBackgroundWorker.Error
いつ呼び出さなければならないかをどのように意味のあるものにしますか?DoWork
カスタムメッセージをユーザーに伝えるさまざまな方法を公開する静的MsgBox
クラスを実装しています (カスタムメッセージフォームを使用)。
公開されたメソッドの 1 つである は、 (カスタム から派生した) をShowProgress
インスタンス化し、何らかのバックグラウンド操作が行われている間、進行状況バーを表示します (ユーザーはその間ずっと操作をキャンセルできます)。モーダル フォームでバックグラウンド タスクを実行するのがぎこちなく聞こえる場合は、次のような署名を検討してください。ProgressMessageBoxForm
MessageBoxForm
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;
}
}
のイベント ハンドラは、カスタム メッセージボックスに渡されたメソッドを呼び出しますBackgroundWorker
。DoWork
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
ので、私は頼りました。Task
Thread
編集
これは問題ではありません。コンパイルされたアプリは爆発しません。実際、デリゲート メソッドでスローされた例外でデバッガーが中断することに混乱しました。以下のコメントで指摘されているように、実行/デバッグはその後も続行でき、実行する予定のエラー処理ロジックはすべて実行されます。BackgroundWorker
どういうわけか例外とデバッガーが続行することを期待していcatch
ましたが、例外がキャッチされ、それでもデバッガーが壊れていることがわかりました。
前にこれを読むべきだった: BackgroundWorker で未処理の例外