如何为有时仅异步的操作创建和实现接口

时间:2019-04-01 00:57:44

标签: c# interface async-await task

假设我有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");
        }
    }
}

这是创建和实现仅有时异步的接口的正确方法吗?我有很多实现短同步操作的类,并担心这会增加很多开销。还有其他我想念的方式吗?

3 个答案:

答案 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");
        }
    }
}