【问题标题】:Is adding async to a method signature a breaking change?将异步添加到方法签名是一项重大更改吗?
【发布时间】:2017-06-06 17:06:22
【问题描述】:

this answer 的作者 Matías Fidemraizer 在解决有关使用 async/await 的问题时,即“所有方法都应该返回 Task 吗?”声称,即使您的方法目前只执行同步操作,它仍然应该返回一个任务,因此如果您稍后让它执行异步操作,“您可以将其转换为实际的异步操作而不会影响整个代码库”。这是有道理的,但如果我真的在等待什么,我必须在方法签名中添加async。所以我们说的是从:

public Task WhateverAsync()
{
    return Task.FromResult(true);
}

public async Task WhateverAsync()
{
    return await AwaitableSomething();
}

在方法签名中添加async 是一项重大更改吗?

【问题讨论】:

  • 你能简单地返回AwaitableSomething,让调用者可以等待吗? // 还是把函数体包装成Task.Run(async () => { ... });? // 所有方法我都用过了,包括在函数定义中加上async,有兴趣看看大佬们怎么说。
  • 您专门链接到回答您问题的帖子,那么您为什么要问这个问题?
  • 它没有回答我的问题。这只是一个声明。
  • @KDecker 为什么你会安排一个线程池线程来启动你的异步操作,你为什么要将异步操作包装在一个毫无意义的异步 lambda 中?所有这些都只会毫无意义地增加开销。
  • 短语“非常具体”意味着它直接解决了重大更改。你能告诉我它是在哪里做的吗?我的眼睛开始疼了,我的浏览器的“查找”功能也无济于事。所以也许我在问答案是否正确。所以呢?不过,很高兴看到您的 cmets 并没有缺少他们通常的贬义“我比所有人都聪明”。

标签: c# .net asynchronous async-await


【解决方案1】:

即使你的方法目前只做同步的东西,它仍然应该返回一个任务

我不同意。如果你的方法是同步的,那么它应该有一个同步的 API。如果您的方法是异步的,那么它应该有一个异步 API。

但是,我同意同步方法应该有一个Task-returning 签名如果它们是在接口/基类中定义的并且很有可能未来的实现/覆盖可能需要使用await

将异步添加到方法签名是一项重大更改吗?

只是添加async?不它不是。考虑在接口中定义的Task-returning 方法的情况:它可以在有或没有async 的情况下实现。

但是,正如我在博客中所描述的那样,有一些可能的 differences in semantics。最值得注意的是异常处理不同。如果您不小心,幼稚的同步实现可能会直接抛出异常,而不是返回错误的任务。一旦你创建了 async 方法,异常就会导致任务出错,并且不再直接抛出。

所以,这确实是一个关于语义可能变化的问题。我认为幼稚的同步语义是错误的,因为该方法具有异步签名。例如,同步方法具有异步签名,因此调用者可以合理地假设它将捕获异常并返回错误任务。所以,如果同步实现有这些(容易引起的)错误,那么添加async在技术上将是一个重大变化。

【讨论】:

  • 嗨,斯蒂芬,非常感谢您的回答——质量一如既往。我在阅读您的博客文章时有一个问题:“使用”标题下的代码示例是否正确?该代码不包含“使用”语句,也与下载无关(您指出)。
  • 我想知道你看到了什么。 :) 对我来说,“使用”下的代码示例中的两种方法都使用using 并使用HttpClient 下载字符串。
  • 我收回我的声明 :) 再看一眼,它显然就在那里。无法解释我之前看到的东西。
【解决方案2】:

不,这不是(真的)重大变化。在方法中添加async 关键字只是对编译器的提示,它应该将其转换为异步状态机。

我正在使用以下代码检查 LINQPad 中方法的公共签名。

var method = typeof(ContainingClass).GetMethod("WhateverAsync");
method.ReturnType.Dump();
method.GetParameters().Length.Dump();
method.GetCustomAttributes().Dump();

前两个转储为您的两种方法返回以下内容。

System.Threading.Tasks.Task
0

意味着它们返回相同类型的值并采用相同数量的参数。它们确实有不同的属性,这在技术上是一个重大变化(但如果代码确实依赖它,那就是垃圾代码)。

第一种方法没有自定义属性,而第二种方法有以下 2 个属性:AsyncStateMachineAttributeDebuggerStepThroughAttribute

在使用反射时需要注意的其他一点是,将 async 关键字添加到方法中,会向其包含类型添加嵌套的私有(生成)类,通常命名为 <WhateverAsync>d__0

【讨论】:

  • 我不同意。如果有人将我的一个类中的方法更改为异步,那么每次调用都会搞砸,除非每次调用都被重构。如果您希望它异步,请制作一个异步版本。
  • @Trey 这里的前提是你有一个从一开始就返回Task的方法,所以只有方法的私有实现才会改变.
  • @Trey -- 这不是作者所说的。不是“将方法更改为异步”。它已经是异步的,因为它返回了一个任务。唯一的问题是添加“async”关键字(其唯一目的是启用内部await)是否是一项重大更改。
  • 啊,我的坏 Rory/Servy。我的眼睛睁开了。 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多