假设我有100多个使用“计算”方法实现公共接口的类。一些类将执行异步(例如,读取文件),而其他实现相同接口的类将执行同步的代码(例如,添加两个数字)。为维护和性能而编写此代码的好方法是什么?
到目前为止,我所阅读的帖子始终建议使async / await方法冒泡给调用者。因此,如果您有一个异步操作,则使调用方异步,然后使其调用方异步,依此类推。因此,这使我认为该接口应该是异步接口。但是,这在使用同步代码实现接口时会产生问题。
我想到的一个主意是在接口中公开2个方法,一个异步和一个同步,以及一个布尔属性,以告知调用方要调用的方法。不过,这看起来真的很丑。
我目前拥有的代码只是一个异步的接口方法。然后,对于同步的实现,它们将代码包装在Task对象中:
using System.IO;
using System.Threading.Tasks;
namespace TestApp
{
interface IBlackBox
{
Task<string> PullText();
}
sealed class MyAsyncBlackBox : IBlackBox
{
public async Task<string> PullText()
{
using (var reader = File.OpenText("Words.txt"))
{
return await reader.ReadToEndAsync();
}
}
}
sealed class MyCachedBlackBox : IBlackBox
{
public Task<string> PullText()
{
return Task.Run(() => "hello world");
}
}
}
这是创建和实现仅有时异步的接口的正确方法吗?我有很多实现短同步操作的类,并担心这会增加很多开销。还有其他我想念的方式吗?
答案 0 :(得分:3)
这是 interfaces 的常见情况。如果您有一个合同需要为 Async Await模式指定task
,在界面中实现Task
。
假设呼叫者将使用await
,则只需放下async
并返回一个Task
。
但是,您需要注意您的例外。它假定 exceptions 被放置在 task 上。因此,为了保持这种状态,呼叫者将期望您必须稍微不同地处理它们。
常用用法
标准async
public async Task<string> PullText()
{
using (var reader = File.OpenText("Words.txt"))
{
return await reader.ReadToEndAsync();
}
}
为受CPU限制的工作返回一个Task
(捕获异常并将其放置在Task
上)
public Task<string> PullText()
{
try
{
return Task.Run(() => DoCpuWork());
}
catch (Exception e)
{
return Task.FromException<string>(e);
}
}
在检测IAsyncStateMachine
public async Task<string> PullText()
{
return await Task.Run(() => DoCpuWork());
}
返回具有简单结果的完成 Task
(捕获异常并将其放置在Task
上)
public Task<string> PullText()
{
try
{
// simplified example
return Task.FromResult("someString");
}
catch (Exception e)
{
return Task.FromException<string>(e);
}
}
还有第三种方法,您可以使用async
关键字,并使用pragma
发出警告,这将为您解决错误语义。对我来说,这有点脏,只是因为它看起来很杂乱并且需要pragma
发出警告,尽管我现在已经看到了定制生产库中使用的警告
#pragma warning disable 1998
public async Task<string> PullText()()
#pragma warning restore 1998
{
return Task.Run(() => "hello world");
}
和
#pragma warning disable 1998
public async Task<string> PullText()()
#pragma warning restore 1998
{
return Task.FromResult("someString");
}
以上所有 注意 都涉及从方法中返回Task<T>
。如果只是想返回Task
,则可以利用具有与上述相同的错误语义的Task.CompletedTask;
。
答案 1 :(得分:1)
通常,在这些情况下,您需要在呼叫之前处理请求并将其传递给“工人”类(例如TestApp)。如果是这种情况,我不明白为什么使用“ IAsyncable”接口可以测试类是否具有异步功能的原因。
if(thisObject is IAscyncAble) {
... call the ansync request.
}
答案 2 :(得分:0)
我最终使用了以下代码:
using System.IO;
using System.Threading.Tasks;
namespace TestApp
{
interface IBlackBox // interface for both sync and async execution
{
Task<string> PullText();
}
sealed class MyAsyncBlackBox : IBlackBox
{
public async Task<string> PullText()
{
using (var reader = File.OpenText("Words.txt"))
{
return await reader.ReadToEndAsync();
}
}
}
sealed class MyCachedBlackBox : IBlackBox
{
public Task<string> PullText() // notice no 'async' keyword
{
return Task.FromResult("hello world");
}
}
}