



Before C# 7 every async
method had to return Task
, Task<T>
or void
. The new C# version extends the range of returned types.
Note: You can view all code examples on GitHub. My environment: Visual Studio 2017 and .NET Framework 4.6.1.
The void-returning methods are recommended only for async
event handlers. It’s because they are fire and forget methods – it means that we don’t care what will be the result of an event.
The recommended return type for any other scenario is either Task
– if a method doesn’t return a value or Task<T>
– when a method returns a value.
ValueTask
There is a new predefined type added to C# 7.0 that can be returned by async
methods – ValueTask
and ValueTask<T>
.
class Example1
{
async ValueTask MethodWithoutReturnValue()
{
await Task.Delay(1);
}
async ValueTask<int> MethodReturningValue()
{
await Task.Delay(1);
return 1;
}
}
Note: To use the ValueTask
one have to install System.Threading.Tasks.Extensions NuGet package.
There is a major difference between Task
and ValueTask
. The Task
is a reference type and requires heap allocation. The ValueTask
is a value type and is returned by value – meaning, no heap allocation. It’s recommended to use ValueTask
when there is a high probability that a method won’t have to wait for async
operations. For example, if a method returns cached or predefined results. This can significantly reduce the number of allocations and result in big performance improvement.
class Example2
{
public async ValueTask<string> GetNameAsync(int id)
{
if (TryGetNameFromCache(id, out var name))
{
return name;
}
name = await GetNameFromDatabaseAsync(id);
AddToCache(id, name);
return name;
}
/* Other methods */
}
There is one downside of using ValueTask
– it’s not compatible with Task
. If there is a method returning Task
and it calls a method returning ValueTask
, there is no way to convert ValueTask
to Task
.
class Example3
{
Task<int> GetValueWithTask() => Task.FromResult(0);
ValueTask<int> GetValueWithValueTask() => new ValueTask<int>(0);
ValueTask<int> GetValue()
{
// This won't compile
// CS0029: Cannot implicitly convert type 'System.Threading.Tasks.Task<int>' to 'System.Threading.Tasks.ValueTask<int>'
// return GetValueWithTask();
return GetValueWithValueTask();
}
async ValueTask<int> GetValueAsync()
{
// But this will work fine
return await GetValueWithTask();
}
}
Custom async return types
It’s also allowed to define custom task-like types. It’s not easy to do it correctly and it’s not expected to be done often.
Here is the link to the roslyn documentation about how to build custom async task types.




Pingback: All C# 7.0 features – C# Today