-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Description
It's common that a method may perform all its work synchronously, and the last action is to call a (possibly asynchronous) method that returns a Task or Task<T>:
public async Task SomeMethodAsync()
{
// some validation or other synchronous work
await ownedObject.DoAsynchronousThings();
}In these cases, the code can be simplified (including the generated code) by removing the async modifier, and the await statement:
public Task SomeMethodAsync()
{
// some validation or other synchronous work
return ownedObject.DoAsynchronousThings();
}However, when we explain this optimization, we must be careful to explain when it can cause bugs. One important case is when the method allocates a Disposable object:
// Danger! Will often throw an AlreadyDisposedException:
public Task SomeMethodAsync()
{
// some validation or other synchronous work
using (var resource = AllocateDisposableResource())
{
return ownedObject.DoAsynchronousThings(resource);
}
}In the case above, the resource is possible still be used by ownedObject.DoAsynchronouseThings after the synchronous method SomeMethodAsync returns. In those cases (and others that manage or capture resources, the async / await keywords ensure that cleanup doesn't happen until the asynchronous work finishes:
// The generated state machine ensures resource cleanup
// only after the asynchronous method has completed its work:
public async Task SomeMethodAsync()
{
// some validation or other synchronous work
using (var resource = AllocateDisposableResource())
{
await ownedObject.DoAsynchronousThings(resource);
}
}