在非异步接口中添加异步

Add async in a non-async interfaces

我有一个遗留的 .NET Framework 应用程序 运行 十年了。

有接口:

interface IService
{
   void Run();
}

并且这个接口在许多现有的 classes 中实现。有一个中央管理器挑选所有实现 IService 的 classes 并在每个 class 上调用 Run() 方法。 (此代码已经可用并按预期运行)。

现在,我想再添加一个 class 来实现同样的 IService,但我希望 Run() 成为 async Task 而不是 [=16] =],因为我正在处理这个新 class 中的 API 个调用。我不想打扰中央管理器的现有功能。

只需以 non-async 方式包装您的新异步功能。

class AsyncService : IService
{
    public void Run()
    {
        myasyncFunction().GetAwaiter().GetResult();
    }
} 

您不能更改您的界面以(可选地)以保持向后兼容性的方式支持异步调用——这是一件好事。

说明

请记住,接口的全部目的是保证所有调用者都可以与该接口的具体实现进行交互,而无需了解具体类型本身。将方法实现为 async 会更改该方法的签名——即通过返回 Task<> 并可能需要一个 await 关键字,具体取决于它的调用方式——因此会破坏接口。您可以更改签名,但根据定义,这会破坏与现有接口的向后兼容性。

选项

鉴于此,针对此问题提供了三种教科书解决方案:

  1. 按照 中的建议,在您现有的同步接口中处理异步调用。这保持了向后兼容性,但消除了异步调用的好处。
  2. 更改接口以使用 async,因此需要更新所有实现。这是最具侵入性的方法,如果您不拥有所有实现,则可能不切实际。
  3. 创建一个新的 async 版本的界面(例如 IServiceAsync),供需要异步功能的实现和调用者使用,如 中所建议。

如果您的首要任务是与现有代码完全向后兼容,而不是异步处理的性能优势,您应该选择第一个选项。

假设您想要异步调用的性能优势,但是,考虑到您正在使用遗留代码,最后一个选项最有意义;如果您不拥有所有实现,则尤其如此。

此答案的其余部分将采用第三个选项。

基本接口

如果合适,您的同步和异步接口可以派生自共享任何非async 方法的公共接口,从而允许它们在这些场景中互换使用。如果您的代码不依赖于任何隐含的 async 方法,这将很有用。你的问题并没有暗示这一点,但我假设你的界面可能比这里包含的更多。

中央经理

您的中央管理器需要更新以查找 IServiceIServiceAsync,并有条件地调用后者,例如 RunAsync()。在这种情况下,只需确保您真正利用了异步功能(例如,通过将这些添加到任务队列并在任务完成时对其进行处理)。否则,您将无法从异步接口中获得任何性能优势。

影响

我知道您的 objective 是为了避免更新您的中央管理器。不幸的是,虽然 利用 API 调用所需的任何异步处理,但无法实现此目的。