BackgroundWorker.RunWorkCompleted - 无法重新抛出异常

时间:2012-11-03 18:57:02

标签: c# exception backgroundworker

我正在为WCF调用(使用BackgroundWorker)编写某种包装器,以防止GUI在调用过程中冻结。它主要是按预期工作,但是当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
  2. 中抛出了类型'System.Exception'的异常   
  3. “在 throw evt.Error
  4. 上抛出类型'System.Exception'的异常   
  5. Main 方法中的 Application.Run(new Form1())中的“TargetInvocationException未处理”
  6.   

我做错了什么?我想在Windows窗体中捕获异常。

2 个答案:

答案 0 :(得分:1)

你应该改变这个:

throw evt.Error;

对此:

MessageBox.Show(evt.Error.Message);

您的异常当前未处理,因为RunWorkerCompleted处理程序稍后运行。它没有在button3_Click中的try / catch中运行。

答案 1 :(得分: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),您甚至可以使用asyncawait

    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