先前的实现同步时如何使用异步代码

时间:2018-08-19 15:51:55

标签: c# interface solid-principles object-composition

最近我正在学习面向对象编程中的组合。在许多有关它的文章中,有一个FileStore类的示例(或者您称它为),该示例实现了以下接口:

interface IStore
{
   void Save(int id, string data);
   string Read(int id);
}

FileStore类实现了此接口,一切都很好。它可以保存和读取文件。现在,教程文章经常说FileStore可以很容易地交换到例如SqlStore,因为它可以实现相同的接口。这样,客户端类可以通过注入SqlStore而不是FileStore来存储到SQL而不是文件系统。听起来很不错,但是当我真正想到它时,我真的不知道如何实现它。 数据库操作是异步完成的很好的选择。为了使用这个事实,我需要返回一个Task而不是Read(int id)方法中的仅字符串。另外,Save(int id,string data)可以返回一个Task。 考虑到这一点,我不能真正将IStore用于我的SqlStore类。

我看到的一种解决方案是使IStore像这样:

interface IStore
{
   Task Save(int id, string data);
   Task<string> Read(int id);
}

然后,我的FileStore类需要进行一些更改,它将使用Task.FromResult(...)。

这个解决方案对我来说似乎很不方便,因为FileStore必须假装一些异步特性。

您将提出什么解决方案? 我很好奇,因为在教程中,一切总是听起来容易且可行的,但是在实际编程中,事情会变得复杂。

2 个答案:

答案 0 :(得分:2)

我已经看到了解决此问题的不同方法,但是选择的方法始终取决于您所处的情况。

  1. 将界面更改为基于Task

如果您没有任何实现(或者可以轻松更改),或者您不想保留任何同步版本,最简单的方法就是更改界面:

interface IStore
{
   Task SaveAsync(int id, string data);
   Task<string> ReadAsync(int id);
}
  1. 扩展界面

另一个选择是将方法的异步版本添加到接口。如果有同步的实现或已经有太多的实现,这可能不是最佳选择。

interface IStore
{
    void Save(int id, string data);
    Task SaveAsync(int id, string data);

    string Read(int id);
    Task<string> ReadAsync(int id);
}
  1. 通过继承创建异步版本

当您不能更改原始接口或并非所有实现都需要异步版本时,此选项最有意义。当然,您需要决定要使用IStore还是IAsyncStore

interface IStore
{
    void Save(int id, string data);
    string Read(int id);
}

interface IAsyncStore : IStore
{
    Task SaveAsync(int id, string data);
    Task<string> ReadAsync(int id);
}

注意:对于I / O操作(在您的示例中FileStoreSqlStore都执行),应该只有异步方法。

答案 1 :(得分:0)

当我自己创建异步方法时,我总是只对可以在新线程中完成的操作这样做,并且这样做是安全的。

因此,如果我知道我可以使 FileStore 类的 Save 和/或 Read 方法在单独的线程中运行,不会出现问题我肯定会更改Tasks的返回值。

如果接口必须完整(如果不是由您的项目创建的,或者其他代码取决于其同步方法),则除了原始接口外,还可以创建方法的异步版本:

interface IStore
{
  void Save(int id, string data);
  Task SaveAsync(int id, string data);
  string Read(int id);
  Task<string> ReadAsync(int id);
}

(Microsoft编码准则建议在任何异步方法的末尾使用后缀Async。)

另一种方法可能是根据您的需要使用异步版本创建一个IStoreAsync接口。

在方法的异步版本中,他们现在可以创建一个调用同步方法的新线程,并等待该线程完成,如下所示:

async Task<string> ReadAsync(int id) {
  string value = await Task.Run(() => Read(id));
  return value;
}

注意,可能有很多更安全,更好的方法来创建新线程,但是Task.Run仅作为示例,用于测试目的。