以下代码编译失败:
async Task Foo (Action<int> onProgressPercentChanged)
{
return Task.Run (() =>
{
for (int i = 0; i < 1000; i++)
{
if (i % 10 == 0) onProgressPercentChanged (i / 10);
}
});
}
由于以下错误:
严重性代码描述项目文件行抑制状态 错误 CS1997 由于“Nutshell.Foo(Action)”是返回“任务”的异步方法,因此返回关键字后不能跟对象表达式。您打算返回“任务”吗?
但这是完全按照书中所示完成的。有什么我想念的吗?
最佳答案
您的方法标记为 async Task
,所以编译器希望它不返回任何东西。如果你想返回一个任务,那么你不应该标记方法 async
.或者,您可以使用 await
在async
里面方法。
// Option 1
Task Foo (Action<int> onProgressPercentChanged)
{
return Task.Run (() =>
{
for (int i = 0; i < 1000; i++)
{
if (i % 10 == 0) onProgressPercentChanged (i / 10);
}
});
}
// Option 2
async Task Foo (Action<int> onProgressPercentChanged)
{
await Task.Run (() =>
{
for (int i = 0; i < 1000; i++)
{
if (i % 10 == 0) onProgressPercentChanged (i / 10);
}
});
}
有关这两者之间区别的更多信息,请参阅我在 eliding async 上的博客文章.
关于其他问题...
使用 .NET 内置的进度报告,而不是自己滚动。
.NET 使用 IProgress<T>
类型,所以你可以使用它:
Task Foo (IProgress<int> progress = null)
{
return Task.Run (() =>
{
for (int i = 0; i < 1000; i++)
{
if (progress != null && i % 10 == 0) progress.Report(i / 10);
}
});
}
通过遵循进度约定,我们允许调用者不请求进度更新,我们还允许使用 Progress<T>
非常自然地编码到 UI 线程:
// Caller, from UI thread
var progress = new Progress<int>(report =>
{
// Handle progress report. This code is already on the UI thread! :)
});
await Foo(progress);
不要使用 Task.Run
在实现中;使用 Task.Run
调用方法。 Using Task.Run
to implement fake-asynchronous methods is an antipattern .我写了更多关于这个 on my blog
应用此修复程序会使 Foo
同步,这是完全正确的,因为它没有实际的异步工作:
void Foo(IProgress<int> progress = null)
{
for (int i = 0; i < 1000; i++)
{
if (progress != null && i % 10 == 0) progress.Report(i / 10);
}
}
// Caller
var progress = new Progress<int>(report => ...);
await Task.Run(() => Foo(progress));
最后,不要在源头限制进度报告;在接收器处限制它们。如果您的代码曾经在不同的 CPU 上运行,那么您在源代码 (i % 10
) 上所做的任何节流都会有问题。最好使用 IProgress<T>
节流的实现。
这很好地简化了您的代码:
void Foo(IProgress<int> progress = null)
{
for (int i = 0; i < 1000; i++)
{
if (progress != null) progress.Report(i / 10);
}
}
关于C# 进度报告,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41650207/