4

我正在为 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 中的“引发了‘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,您可以使用 Tasks。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使用。

于 2012-11-03T19:34:10.707 回答