Reputation: 43
The following code fails to compile:
async Task Foo (Action<int> onProgressPercentChanged)
{
return Task.Run (() =>
{
for (int i = 0; i < 1000; i++)
{
if (i % 10 == 0) onProgressPercentChanged (i / 10);
}
});
}
due to the following error:
Severity Code Description Project File Line Suppression State Error CS1997 Since 'Nutshell.Foo(Action)' is an async method that returns 'Task', a return keyword must not be followed by an object expression. Did you intend to return 'Task'?
but this is done exactly as shown in the book. Is there something I'm missing?
Upvotes: 1
Views: 5323
Reputation: 456407
Your method is marked async Task
, so the compiler expects it to not return anything. If you want to return a task, then you should not mark the method async
. Alternatively, you could use await
inside the async
method.
// 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);
}
});
}
For more about the difference between these two, see my blog post on eliding async.
On to the other problems...
Use the progress reporting built-in to .NET instead of rolling your own.
.NET uses the IProgress<T>
type, so you can just use that:
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);
}
});
}
By following the progress conventions, we allow callers to not request progress updates, and we also allow a very natural marshalling to the UI thread using Progress<T>
:
// Caller, from UI thread
var progress = new Progress<int>(report =>
{
// Handle progress report. This code is already on the UI thread! :)
});
await Foo(progress);
Don't use Task.Run
in the implementation; use Task.Run
to call the method. Using Task.Run
to implement fake-asynchronous methods is an antipattern. I write more about this on my blog as well.
Applying this fix makes Foo
synchronous, which is exactly correct since it does no actual asynchronous work:
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));
And finally, don't throttle progress reports at the source; throttle them at the receiver. If your code ever runs on different CPUs, then any throttling you do at the source (i % 10
) is going to be problematic. It's better to use an IProgress<T>
implementation that throttles.
This nicely simplifies your code to:
void Foo(IProgress<int> progress = null)
{
for (int i = 0; i < 1000; i++)
{
if (progress != null) progress.Report(i / 10);
}
}
Upvotes: 8
Reputation: 3329
Instead of return
you should await
your Task.Run
statement
async Task Foo(Action<int> action)
{
await Task.Run(() => /*...*/);
}
or, if you prefer to return Task
, you should not mark the method as async
Task Foo(Action<int> action)
{
return Task.Run(() => /*...*/);
}
Upvotes: 2
Reputation: 3839
Remove return statement in method. Your method shouldn't return anything.
Upvotes: 1