【问题标题】:Why/How Does BackgroundWorker Work Without DoWorkEventArgs?为什么/如何在没有 DoWorkEventArgs 的情况下使用 BackgroundWorker?
【发布时间】:2018-03-20 18:29:54
【问题描述】:

以下是我正在处理的一个类的精简版(WinForms 项目的一部分):

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)
    }
}

现在,DoWork 事件处理程序的方法签名通常是这样的:

private void retrieveInvoices(object sender, DoWorkEventArgs e)

但是,正如您在上面看到的,我的retrieveInvoices 方法的签名与该签名不匹配。因此,我预计它会失败(要么不编译,要么只是在 UI 线程上运行 retrieveInvoices,阻止它,而不是在后台工作人员中)。令我惊讶的是,它似乎正在工作,但由于我所见过的 BackgroundWorker 示例都没有这样做,所以我仍然认为我一定做错了什么。但我是吗,为什么?

【问题讨论】:

  • 是的,它确实与此匹配:(sender, e) =&gt;
  • 您可以将e 传递给您的retrieveInvoices() 方法。

标签: c# backgroundworker


【解决方案1】:

行:

worker.DoWork += (sender, e) => this.retrieveInvoices(this.projects); 

引入带有参数(object sender, DoWorkEventArgs e) 的委托,该委托使用参数projects 调用方法retrieveInvoices。没有语法不匹配。

这相当于:

worker.DoWork += (sender, e) => { this.retrieveInvoices(this.projects); }

void doSomething(object sender, System.ComponentModel.DoWorkEventArgs e)
{
    this.retrieveInvoices(this.projects);
}

worker.DoWork += doSomething;

要将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) =&gt; 位正在将接下来的内容变成匿名方法?即使我在调用retrieveInvoices 周围没有大括号?
  • (sender,e) =&gt; 是 lambda 的语法。它不会转换匿名方法旁边的内容,它本身就是一个方法。 =&gt; 之后是方法的body
  • 好的,我想我现在明白了,谢谢。至于BackgroundWorker 不结合多个异步操作的观点,我认为您的意思是添加多个处理程序(例如worker.DoWork += methodA; worker.DoWork += methodB)将导致处理程序一个接一个地运行,而不是并行运行。
  • 不,我的意思是即使让它工作也太复杂了——你必须使用 completion 处理程序来启动下一个 BGW,将第一个结果作为状态传递.然后第二个完成处理程序将不得不尝试启动第三个等等。不要介意错误处理。不过,在我发布的问题中,只是三个await someAsyncMethod() 一个接一个地调用
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-06
  • 2013-06-03
  • 1970-01-01
  • 1970-01-01
  • 2017-02-18
  • 2021-04-07
  • 2015-08-29
相关资源
最近更新 更多