4

呼び出しの進行中に GUI がフリーズしないようにするために、(BackgroundWorker を使用して) WCF 呼び出し用のある種のラッパーを作成しています。ほとんど正常に動作していますが、WCF 呼び出しが例外をスローしているときに BackgroundWorker に問題があります。DoWork で例外が発生した場合、RunWorkCompleted で検出できますが、GUI に再スローしても機能しません。これはうまくいくはずだと人々が言及している多くのスレッドを読みました。

ラッパーのコード (WCF 呼び出しは、スローされる例外によって象徴されていることに注意してください):

private void GetSomething(Action<IEnumerable<int>> completedAction)
{
    BackgroundWorker b = new BackgroundWorker();

    b.DoWork += (s, evt) => { throw new Exception(); evt.Result = new List<int> { 1, 2, 3 }; };

    b.RunWorkerCompleted += (s, evt) =>
    {
        if (evt.Error == null && completedAction != null)
        {
           completedAction((IEnumerable<int>)evt.Result);
        }
        else if(evt.Error != null)
        {
           throw evt.Error;
        }
    };

    b.RunWorkerAsync();
}

Windows フォームでのコードの呼び出し:

private void button3_Click(object sender, EventArgs e)
{
    try
    {
        GetSomething(list =>
        {
           foreach (int i in list)
           {
              listView1.Items.Add(new ListViewItem(i.ToString()));
           }
        });
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

これをデバッグしているときに、次のようになります。

  1. DoWork で「タイプ 'System.Exception' の例外がスローされました」
  2. throw evt.Errorで「タイプ 'System.Exception' の例外がスローされました」
  3. MainメソッドのApplication.Run(new Form1())で「TargetInvocationException が処理されませんでした」

私は何を間違っていますか?Windowsフォームで例外をキャッチしたいと思います。

4

2 に答える 2

1

これを変更する必要があります:

throw evt.Error;

これに:

MessageBox.Show(evt.Error.Message);

RunWorkerCompletedハンドラーが後で実行されているため、例外は現在処理されていません。にあるtry/catch内で実行されていませんbutton3_Click

于 2012-11-04T18:35:39.350 に答える
0

イベントb.RunWorkerCompletedは、エラー処理を行う場所です。Action<Exception>次のようなエラー処理を行うために渡すことができます

private void GetSomething(Action<IEnumerable<int>> completedAction, Action<Exception> exceptionAction)
{
    BackgroundWorker b = new BackgroundWorker();

    b.DoWork += (s, evt) => { throw new Exception(); evt.Result = new List<int> { 1, 2, 3 }; };

    b.RunWorkerCompleted += (s, evt) =>
    {
        if (evt.Error == null && completedAction != null)
            completedAction((IEnumerable<int>)evt.Result);
        else if(evt.Error != null)
            exceptionAction(evt.Error);
    };

    b.RunWorkerAsync();
}

ただし、これは醜くなる傾向があります。.Net 4 または 4.5 を使用している場合は、タスクに頼ることができます。Task<TResult>は、まさにその場合のために作成されました。

Task<IEnumerable<int>> GetSomething()
{
    return Task.Factory.StartNew(() => { 
        Thread.Sleep(2000);
        throw new Exception(); 
        return (new List<int> { 1, 2, 3 }).AsEnumerable(); 
        });
}

Task基本的にはシグナル構造であり、

  • .Resultプロパティ_
  • .Exceptionプロパティ_
  • .ContinueWith()メソッド_

内部で、障害状態 (例外がスローされた)ContinueWith()かどうかを確認できます。Task

次のように使用できます

    private void button3_Click(object sender, EventArgs e)
    {
        GetSomething()
            .ContinueWith(task =>
                {
                    if (task.IsCanceled)
                    {
                    }
                    else if (task.IsFaulted)
                    {
                        var ex = task.Exception.InnerException;
                        MessageBox.Show(ex.Message);
                    }
                    else if (task.IsCompleted)
                    {
                        var list = task.Result;
                        foreach (int i in list)
                        {
                            listView1.Items.Add(new ListViewItem(i.ToString()));
                        }
                    }
                });
    }

.Net 4.5 と C#5 を使用している場合 (VS2012 または VS2010 と Async CTP が必要です)、頼ることもできasyncますawait

    private async void button3_Click(object sender, EventArgs e)
    {
        try
        {
            var list = await GetSomething();
            foreach (int i in list)
            {
                listView1.Items.Add(new ListViewItem(i.ToString()));
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

...そして、すべての魔法はコンパイラによって行われます。使い慣れた方法で使用できることに注意してくださいtry catch

于 2012-11-03T19:34:10.707 に答える