C# 为什么/如何让BackgroundWorker在没有DoworkerVentargs的情况下工作?
以下是我在WinForms项目中处理的类的精简版本:C# 为什么/如何让BackgroundWorker在没有DoworkerVentargs的情况下工作?,c#,backgroundworker,C#,Backgroundworker,以下是我在WinForms项目中处理的类的精简版本: class ReportBuilder { private List<Project> projects; private List<Invoice> invoices; private MyAPI apiObject; public ReportBuilder(MyAPI apiAccess, List<Project> selectedProjects){
class ReportBuilder {
private List<Project> projects;
private List<Invoice> invoices;
private MyAPI apiObject;
public ReportBuilder(MyAPI apiAccess, List<Project> selectedProjects){
this.apiObject = apiAccess;
this.projects = selectedProjects;
}
public void DownloadData(){
BackgroundWorker workerThread = new BackgroundWorker();
workerThread.DoWork += (sender, e) => this.retrieveInvoices(this.projects); // yes, the parameter is unnecessary in this case, since the variable is in scope for the method anyway, but I'm doing it for illustrative purposes
workerThread.RunWorkerCompleted += receiveData;
workerThread.RunWorkerAsync();
}
private void retrieveInvoices(List<Project> filterProjects){
Notification status;
if (filterProjects == null){this.invoices = this.apiObject.GetInvoices(out status);}
else {this.invoices = this.apiObject.GetInvoices(filterProjects, out status);}
}
private void receiveData(Object sender, RunWorkerCompletedEventArgs e){
// display a save file dialog to the user
// call a method in another class to create a report in csv format
// save that csv to file
// ... ideally, this method would to have access to the 'status' Notification object from retrieveInvoices, but it doesn't (unless I make that an instance variable)
}
}
但是,正如您在上面看到的,我的retrieveInvoices方法的签名与此不匹配。因此,我预计它要么不编译,要么只是在UI线程上运行retrieveInvoices,阻止它,而不是在后台工作程序中。令我惊讶的是,它似乎起了作用,但由于我所看到的BackgroundWorker示例都没有这样做,我仍然认为我一定是做错了什么。但我是吗?为什么?行:
worker.DoWork += (sender, e) => this.retrieveInvoices(this.projects);
介绍一个具有参数的委托对象发送方DoWorkEventArgs e,该委托使用参数项目调用方法retrieveInvoices。没有语法不匹配
这相当于:
worker.DoWork += (sender, e) => { this.retrieveInvoices(this.projects); }
或
要将retrieveInvoices用作实际的事件处理程序,您必须编写:
worker.DoWork += retrieveInvoices;
这将导致不匹配
顺便说一句,BackgroundWorker已经过时了。可以使用Task.Run、async/await和IProgress完成它所做的任何事情以及更多的事情。例如,BGW不能用于组合多个异步操作。使用'async/await'也很容易,例如:
async Task<Report> RunReport(Project[] projects, IProgress<string> progress)
{
var data= await retrieveInvoices(projects);
progress.Report("Invoices retrieved");
var report=await render(data);
progress.Report("Report rendered");
await SaveReport(report);
progress.Report("Report saved");
return report;
}
//...
Progress<string> progress=new Progress<string>(msg=>statusBar1.Text=msg);
await RunReport(projects);
是的,它确实与此匹配:sender,e=>您可以将e传递给retrieveInvoices方法。那么,sender,e=>bit正在将接下来的内容转换为匿名方法?尽管我对retrieveInvoices?sender的调用没有花括号,但e=>是lambda的语法。它不会转换匿名方法旁边的内容,它是一个方法本身。=>之后的内容是方法的主体OK,我想我现在明白了,谢谢。至于BackgroundWorker不合并多个异步操作的观点,我认为您的意思是添加多个处理程序,例如worker.DoWork+=methodA;worker.DoWork+=methodB将导致处理程序一个接一个地运行,而不是并行运行。不,我的意思是,即使让它工作起来也太复杂了-您必须使用完成处理程序启动下一个BGW,将第一个结果作为状态传递。然后,第二个完成处理程序必须尝试启动第三个完成处理程序,等等。更不用说错误处理了。但在我发布的问题中,只有三个wait someasynchmethod调用一个接一个
worker.DoWork += retrieveInvoices;
async Task<Report> RunReport(Project[] projects, IProgress<string> progress)
{
var data= await retrieveInvoices(projects);
progress.Report("Invoices retrieved");
var report=await render(data);
progress.Report("Report rendered");
await SaveReport(report);
progress.Report("Report saved");
return report;
}
//...
Progress<string> progress=new Progress<string>(msg=>statusBar1.Text=msg);
await RunReport(projects);