user2921851
user2921851

Reputation: 990

Returning a Task from a base method

I am still in the process of getting a handle on asynchronous programming and a bit puzzled on how to modify the following:

base virtual method:

public virtual async Task OnSuspendingAsync(object s, SuspendingEventArgs e)
{
     // do some housekeeping actions upon suspending in the base class

     await Task.Yield();
}

overriding method:

public override Task OnSuspendingAsync(object s, SuspendingEventArgs e)
{
     // do some housekeeping actions on exiting/suspending

     return base.OnSuspendingAsync(s, e);
}

the question:

Now if I use an awaitable in the overridden method (almost unavoidable), I need to change public override Task to public override async Task by adding the async keyword as illustrated with a real example below.

public override async Task OnSuspendingAsync(object s, SuspendingEventArgs e)
{
   try
   {
     // tidy up app temporary folder on exit
     await ApplicationData.Current.ClearAsync(ApplicationDataLocality.Temporary);
   }
   catch { }

   return base.OnSuspendingAsync(s, e); // this is wrong with awaitable!
}

In this case what should be the return base.OnSuspendingAsync(s, e); change to (and why) which Visual Studio is rightly flagging as an error?

Upvotes: 1

Views: 1730

Answers (1)

Peter Duniho
Peter Duniho

Reputation: 70652

You should await the result:

public override async Task OnSuspendingAsync(object s, SuspendingEventArgs e)
{
   try
   {
     // tidy up app temporary folder on exit
     await ApplicationData.Current.ClearAsync(ApplicationDataLocality.Temporary);
   }
   catch { }

   await base.OnSuspendingAsync(s, e);
}

Before you had await in the method, it was fine for the method to run synchronously. You could do your synchronous logic, and then call the asynchronous base method and just pass the returned Task object it uses to track its progress as your own return value.

But once you introduce await into your method, now your own method is asynchronous. It will return at the very first await, and the Task returned to the caller represents the entire sequence of work your method will perform.

You want to make sure that Task is not indicating a completion until the base method's Task itself has indicated a completion. So the last thing you need to do in your now-asynchronous method is await the result of the base method. In doing so, your method effectively doesn't terminate until after that happens, and thus its own Task object (representing its own work) will not indicate completion until the base method's does.

Note that this is not the only possible way to address your scenario. But IMHO it is the most readable and simple to implement.


ADDENDUM:

I thought the above was sufficient to explain why await is used, but perhaps not. To elaborate on the above, and in particular to answer the question "why is return replaced by await?":

  1. It's important to understand that the return statement isn't literally being replaced. In some contexts, return await is correct. I.e. when the async method does in fact return a value and that value is produced by some awaitable operation.

  2. So where did the return statement go? Well: when you create an async method, the return type changes. Instead of being the actual value returned by the method with a return statement, the method's return type must be void, Task, or Task<T> (where T is of course some actual type or a defined type parameter).

    Here we must use Task because that's the method signature we have to comply with. But note that we use Task when no actual value is returned (i.e. the method would otherwise be void). So our async method must not return anything. You can write return;, but not return <some value>;. I opted to omit the return entirely, but if you prefer having it, you can add return; after the second await...

  3. Why was await added. I hope this is more clear, per my explanation above. It's what causes the execution of the method to be temporarily suspended while the base.OnSuspendingAsync() method is doing it's work (asynchronously). It works with async to indicate to the compiler a point in the method when it can return, and then where execution will resume next.

    Using await is what ensures the Task returned by your OnSuspendingAsync() override does not indicate completion until the base implementation has itself completed.

Upvotes: 6

Related Questions