在基类中拥有虚拟异步方法是否可行?

66

我正在处理一些代码,其中有两个类的逻辑和代码非常相似。它们都有一个protected async void LoadDataAsync()方法。
目前,我正在重构代码,并考虑将共享的逻辑移动到基类中。
在基类中使用virtual async方法,在派生类中重写它,这样可以吗?
是否存在任何问题?
我的代码如下:

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

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

已经有类似但不完全相同的问题被提出了。


8
首先,你需要一个返回类型...请注意 async 作为修饰符仅适用于方法的实现 - 它不是签名的一部分。 - Jon Skeet
谢谢@Jon Skeet,返回类型有错别字。 - Samvel Siradeghyan
可能是如何在C#5.0中强制使用异步子覆盖的重复问题。 - Samvel Siradeghyan
我认为这个问题应该被关闭,因为它是重复的。 - Samvel Siradeghyan
3个回答

101

简短回答

  • virtual方法可以标记为async
  • abstract方法不能标记为async

原因是async实际上不是方法签名的一部分。它只是告诉编译器如何处理方法体的编译(不适用于重写方法)。由于抽象方法没有方法体,因此将async修饰符应用于它是没有意义的。

详细回答

如果基类提供了方法的默认实现但不需要执行任何工作,则建议使用以下方法而不是当前的基类签名。

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

你的实现需要进行以下关键更改:

  1. 将返回值从void更改为Task(记住async实际上并不是返回类型的一部分)。与返回void不同,当返回Task时,调用代码可以执行以下任何一种操作:
  • 等待操作完成
  • 检查任务状态(已完成、已取消、已故障)
  1. 避免使用async修饰符,因为该方法不需要await任何内容。相反,只需返回一个已完成的Task实例。重写此方法的方法仍然可以使用async修饰符(如果需要)。

1
“return Task.Run()” 更合适吧?派生类可以使用“await base.LoadDataAsync()”,这应该与使用“Task.FromResult()”相同。这将使实际行为更接近方法的“异步”命名约定,这意味着该方法可能正在异步执行某些操作。它还将消除返回默认对象的需要。 - HotN

22

-8
同意@Sam的观点。
我抛出异常只是为了确保实际逻辑得到执行。更符合我的要求,仅此而已。
protected virtual Task LoadDataAsync()
{
    throw new NotImplementedException();
}

如果你想强制一个派生类实现一个给定的方法,你应该使用 abstract 关键字,它是专门为此目的设计的,而不是使用这个 virtual 解决方案。 - zcoop98

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接