可以使用IProgress
或BackgroundWorker.ReportProgress
从异步任务传递信息:
class Approach1 : UserControl
{
enum Stage { INIT, STATUS, DATA, TIME, ... }
struct ProgressObject
{
int StatusCode;
int SecondsRemaining;
IList<int> Data;
Stage CurrentStage;
}
TextBox Status;
async Task DoWork1(IProgress<ProgressObject> progress)
{
await Task.Run( () =>
{
progress.Report(new ProgressObject(0, 0, null, Stage.INIT));
int code = DoSomething();
progress.Report(new ProgressObject(code, 0, null, Stage.STATUS));
IList<int> Data = ...;
progress.Report(new ProgressObject(0, 0, Data, Stage.DATA));
int Seconds = ...;
progress.Report(new ProgressObject(0, time, null, Stage.TIME));
});
}
void ReportProgress(ProgressObject progress)
{
switch (progress.CurrentStage)
{
case Stage.CODE:
Status.Text = DecodeStatus(progress.StatusCode);
break;
// And so forth...
}
}
async void Caller1(object sender, EventArgs e)
{
var progress = new Progress<ProgressObject>(ReportProgress);
await DoWork2(progress);
}
}
但是,这也可以通过将委托传递给UI对象的BeginInvoke
方法(Invoke
,如果我们想要阻止)来完成:
class Approach2 : UserControl
{
Textbox Status;
int StatusCode
{
set
{
BeginInvoke(new Action( () => Status.Text = DecodeStatus(value));
}
}
// Imagine several other properties/methods like the above:
int SecondsRemaining;
void UpdateData(IList<int> data);
async Task DoWork2()
{
await Task.Run( () =>
{
StatusCode = DoSomething();
UpdateData(...);
SecondsRemaining = ...;
});
}
async void Caller2(object sender, EventArgs e)
{
await DoWork1();
}
}
专用进度报告机制是否优先于Invoke
?如果,为什么呢?是否有任何可能的问题&#39;两种方法都产生了什么?
恕我直言,与Invoke
接受具有多个字段的进度结构相比,ReportProgress
方式更简单/需要的代码更少,特别是如果在任务的多个阶段报告进度并且因此,报告方法需要分支到给定阶段的适当报告。
答案 0 :(得分:2)
你应该从你的努力中得到一个提示,使Approach2实际编译。花了一段时间,没有?我看到你反复编辑代码片段了。你得到的另一个提示是,到达那里的唯一方法是从UserControl派生你的类。
Begin / Invoke()存在哪个问题,它只能在您有权访问Control对象时才能工作。库中的代码经常(并且应该)不知道UI的样子。它可能甚至没有在Winforms中实现,例如可以在WPF或Universal应用程序中使用。
进度和LT;&GT;它也适用于那些GUI类库,它使用更通用的方式来正确同步。由SynchronizationContext.Current属性提供,它依赖于GUI类库来安装提供程序。 Winforms安装的那个,WindowsFormsSynchronizationContext,在其Post()方法中自动调用BeginInvoke(),在其Send()方法中调用Invoke()。也是使async / await代码独立于UI实现的机制。
Progress&lt;&gt;有一个缺点,它可能完全无法以非常难以诊断的方式完成工作。对象必须由在应用程序的UI线程上运行的代码创建。如果不是,那么SynchronizationContext.Current没有值,并且ProgressChanged事件将在任意线程池线程上触发。 Kaboom如果您尝试使用事件处理程序更新UI,则您不会知道原因,因为异常发生在远离错误的代码中。
但是可以肯定的是,如果您将类硬编码为从System.Windows.Forms.UserControl派生,那么您几乎没有使用Progress&lt;&gt ;.除了感觉良好的感觉,当你将它移植到另一个GUI类库时,你将会做更少的工作。