最近我正在学习面向对象编程中的组合。在许多有关它的文章中,有一个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必须假装一些异步特性。
您将提出什么解决方案? 我很好奇,因为在教程中,一切总是听起来容易且可行的,但是在实际编程中,事情会变得复杂。
答案 0 :(得分:2)
我已经看到了解决此问题的不同方法,但是选择的方法始终取决于您所处的情况。
Task
。如果您没有任何实现(或者可以轻松更改),或者您不想保留任何同步版本,最简单的方法就是更改界面:
interface IStore
{
Task SaveAsync(int id, string data);
Task<string> ReadAsync(int id);
}
另一个选择是将方法的异步版本添加到接口。如果有同步的实现或已经有太多的实现,这可能不是最佳选择。
interface IStore
{
void Save(int id, string data);
Task SaveAsync(int id, string data);
string Read(int id);
Task<string> ReadAsync(int id);
}
当您不能更改原始接口或并非所有实现都需要异步版本时,此选项最有意义。当然,您需要决定要使用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操作(在您的示例中FileStore
和SqlStore
都执行),应该只有异步方法。
答案 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
仅作为示例,用于测试目的。