以下代码无法编译:
async Task Foo (ActiononProgressPercentChanged) { return Task.Run (() => { for (int i = 0; i < 1000; i++) { if (i % 10 == 0) onProgressPercentChanged (i / 10); } }); }
由于以下错误:
严重性代码说明项目文件行抑制状态错误CS1997由于'Nutshell.Foo(Action)'是返回'Task'的异步方法,因此return关键字后不能跟随对象表达式。您是否打算返回“任务”?
但这完全按照书中所示完成。有什么我想念的吗?
您的方法已标记为async Task
,因此编译器希望它不返回任何内容。如果要返回任务,则不应标记该方法async
。或者,您可以await
在async
方法内部使用。
// Option 1 Task Foo (ActiononProgressPercentChanged) { return Task.Run (() => { for (int i = 0; i < 1000; i++) { if (i % 10 == 0) onProgressPercentChanged (i / 10); } }); } // Option 2 async Task Foo (Action onProgressPercentChanged) { await Task.Run (() => { for (int i = 0; i < 1000; i++) { if (i % 10 == 0) onProgressPercentChanged (i / 10); } }); }
有关这两者之间的区别的更多信息,请参见我关于消除异步的博客文章。
关于其他问题...
使用内置于.NET的进度报告,而不是自己滚动。
.NET使用该IProgress
类型,因此您可以使用该类型:
Task Foo (IProgressprogress = null) { return Task.Run (() => { for (int i = 0; i < 1000; i++) { if (progress != null && i % 10 == 0) progress.Report(i / 10); } }); }
通过遵循进度约定,我们允许调用者不请求进度更新,并且还允许使用Progress
以下命令对UI线程进行非常自然的编组:
// Caller, from UI thread var progress = new Progress(report => { // Handle progress report. This code is already on the UI thread! :) }); await Foo(progress);
不要Task.Run
在实现中使用;用于Task.Run
调用方法。 使用Task.Run
实施假异步方法是一种反模式。我也在我的博客上写了更多有关此的内容。
应用此修复程序可以使Foo
同步,这是完全正确的,因为它没有实际的异步工作:
void Foo(IProgressprogress = null) { for (int i = 0; i < 1000; i++) { if (progress != null && i % 10 == 0) progress.Report(i / 10); } } // Caller var progress = new Progress (report => ...); await Task.Run(() => Foo(progress));
最后,不要从源头上限制进度报告;在接收器处调节它们。如果您的代码曾经在不同的CPU上运行,那么您在源(i % 10
)上进行的任何限制都会成为问题。最好使用限制的IProgress
实现。
这很好地简化了您的代码以:
void Foo(IProgressprogress = null) { for (int i = 0; i < 1000; i++) { if (progress != null) progress.Report(i / 10); } }