65

I am working with some code, where I have 2 classes with very similar logic and code. I have protected async void LoadDataAsync() method on both classes.
Currently I am refactoring it and thinking to move shared logic to base class.
Is it OK to have virtual async method on base class and override it on derived classes?
Are there any issues with it?
My code looks like this:

public class Base
{
   protected virtual async void LoadDataAsync() {}
}

public class Derived : Base
{
   protected override async void LoadDataAsync()
   {
       // awaiting something
   }
}

Similar (but not same) question was already asked.

monty
  • 1,543
  • 14
  • 30
Samvel Siradeghyan
  • 3,523
  • 3
  • 30
  • 49

3 Answers3

99

Short answer

  • A virtual method may be marked as async
  • An abstract method cannot be marked as async

The reason for this is async is not actually part of the method signature. It simply tells the compiler how to handle the compilation of the method body itself (and does not apply to overriding methods). Since an abstract method does not have a method body, it does not make sense to apply the async modifier.

Long answer

Rather than your current signature in the base class, I would recommend the following if the base class provides a default implementation of the method but does not need to do any work.

protected virtual Task LoadDataAsync() {
  return Task.CompletedTask;
}

The key changes from your implementation are the following:

  1. Change the return value from void to Task (remember async is not actually part of the return type). Unlike returning void, when a Task is returned calling code has the ability to do any of the following:
  • Wait for the operation to complete
  • Check the status of the task (completed, canceled, faulted)
  1. Avoid using the async modifier, since the method does not need to await anything. Instead, simply return an already-completed Task instance. Methods which override this method will still be able to use the async modifier if they need it.
zcoop98
  • 2,590
  • 1
  • 18
  • 31
Sam Harwell
  • 97,721
  • 20
  • 209
  • 280
  • 1
    wouldn't `return Task.Run()` be more appropriate? The derived classes can use `await base.LoadDataAsync()` which should behave the same as using `Task.FromResult()`. This would make the actual behavior more closely match the method's "Async" naming convention, which implies the method may be doing something asynchronously. It also would remove the need for returning a default object. – HotN Nov 03 '15 at 18:25
21

Yes, it's fine, but you should use async Task instead of async void. I have an MSDN article that explains why.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
-8

Agree with @Sam.
I throw exception just to ensure that actual logic is implemented. Better fits my requirement, that's all.

protected virtual Task LoadDataAsync()
{
    throw new NotImplementedException();
}
HSPR
  • 1
  • 1
  • If you're looking to force a derived class to implement a given method, you should use the `abstract` keyword, which is [designed explicitly for this purpose](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/abstract), instead of using this `virtual` solution. – zcoop98 Aug 09 '22 at 22:13