我正在为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);
}
}
调试时,我得到:
- “在DoWork
中抛出了类型'System.Exception'的异常- “在 throw evt.Error
上抛出类型'System.Exception'的异常- Main 方法中的 Application.Run(new Form1())中的“TargetInvocationException未处理”
醇>
我做错了什么?我想在Windows窗体中捕获异常。
答案 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),您甚至可以使用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
。