微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – BackgroundWorker.RunWorkCompleted – 无法重新抛出异常

我正在为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. “Exception of type ‘System.Exception’ was thrown” in DoWork
  2. “Exception of type ‘System.Exception’ was thrown” at throw evt.Error
  3. “TargetInvocationException was unhandled” at Application.Run(new Form1()) in the Main method

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

解决方法

事件b.RunWorkerCompleted是您应该进行错误处理的地方.您可以传递一个Action< Exception>做错误处理就像

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

    b.DoWork += (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,则可以使用“任务”.任务< TResult>是为了这种情况而创建的:

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

任务基本上是一个信号构造

> a.结果财产
> .Exception属性
> a .ContinueWith()方法

在ContinueWith()中,您可以检查Task是否处于故障状态(Exception被抛出).

你可以像使用它一样

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),您甚至可以使用异步并等待

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.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐