Runtime: 异步后缀

创建于 2018-07-23  ·  19评论  ·  资料来源: dotnet/runtime

从原始问题迁移https://github.com/dotnet/core/issues/1464

_ @lukepuplett于 4 月 19 日发表评论_

2018 年应该使用 Async 后缀约定吗?

我很抱歉提出这个问题,这不是一个问题,它值得商榷,但这可能是最好的论坛。

.NET 和其他开源库是否应该继续在等待项上附加Async

一般的

我这里的团队已经开始放弃它了; 我一直想知道这是否会发生,但在我读到这篇文章之前认为还为时过早。

https://docs.particular.net/nservicebus/upgrades/5to6/async-suffix

我们认为 2018 年的适当指导是什么?

谢谢

_ @deerchao于 5 月 29 日发表评论_
从未在我的代码中使用过 Async 后缀。

_ @MarcoTheFirst 11 天前发表评论_
我完全同意。 有足够的迹象表明方法是异步的:

  1. 返回类型是任务/任务<>
  2. 代码完成提供了一个等待提示
  3. IDE 将通过绿色下划线警告您
  4. 当没有等待任务时,编译器会发出警告。

仅当您的 API 提供两种风格的相同功能时,使用 Async 后缀才有意义:

GetObject()
GetObjectAsync()

对于任何其他用例,使用 Async-suffix 就像将 Lazy-suffix 添加到任何 Lazy<> 属性一样愚蠢:

public Lazy<Customer> CustomerLazy.

如果您不这样做,请不要使用 Async 后缀 :-)

_ @lukepuplett 6 天前发表评论_
感谢您的回答。

为了准确起见,第一个并不完全正确,编译器通过查找等待类型来工作,该类型是任何带有TaskAwaiter GetAwaiter()的类型。

Design Discussion api-suggestion area-System.Threading.Tasks

最有用的评论

假设您正在审查 PR。 假设他们添加了这行代码:
```c#
var 结果 = GetResult();

Is it sync or async? Did someone forget to `await` the result? What's the return type? None of these things are apparent. Now let's compare:
```c#
var result = GetResultAsync();

……这很明显。 让它显而易见。 在您了解很多的特定场景中,没有后缀可能没有问题。 有后缀总是没有问题的。 这只是一个积极的。

作为一个经常审查代码人,人们可以从我冰冷的死手指中撬出Async后缀。 他们最好希望在他们尝试的时候没有出现僵硬。

所有19条评论

它之所以存在,是因为您不能在 C# 等语言中通过返回类型重载。 如果可以排除以后添加方法同步版本的可能性,我看到的场景我想不出任何理由使用后缀。 这取决于上下文。
另一个数据点是 C# 的async Main ,它不是MainAsync ,因为不需要维护它的同步版本。

假设您正在审查 PR。 假设他们添加了这行代码:
```c#
var 结果 = GetResult();

Is it sync or async? Did someone forget to `await` the result? What's the return type? None of these things are apparent. Now let's compare:
```c#
var result = GetResultAsync();

……这很明显。 让它显而易见。 在您了解很多的特定场景中,没有后缀可能没有问题。 有后缀总是没有问题的。 这只是一个积极的。

作为一个经常审查代码人,人们可以从我冰冷的死手指中撬出Async后缀。 他们最好希望在他们尝试的时候没有出现僵硬。

.NET 和其他开源库是否应该继续在可等待对象上附加异步?

是的。

我期待在 2019 年再次提出这个问题。

更严肃的一点是, @NickCraver或多或少一针见血。 编译器警告很好,但代码应该是可读和富有表现力的,而不依赖于工具来确切地知道发生了什么。 Nick 的例子很棒,但是当你有Task并且根本没有返回值时,它对我来说更有吸引力。

代码库不可能是 100% 异步的。 那将是愚蠢的,而且确实 async 并非一直都是正确的答案。 因为我们总是有异步和同步代码,所以目前最清晰的表达方式是使用后缀。

您当然可以自由地做任何您认为最适合您的项目的事情。 Octokit是一个不使用后缀的项目示例。

我认为最好的选择是一致性。 对于 CoreFX,这意味着在可预见的未来使用异步后缀。

异步对控制流、性能等具有如此深远的影响,以至于在调用站点上提供额外的视觉提示是必要的和赞赏的,IMO

不管“正确”的选择如何,这不是哥斯拉大小的突破性变化吗? 也许如果我们将 .NET Core 的版本提高到11那么它可能没问题......但仍然如此。

也许如果我们将 .NET Core 的版本提升到 11 可能就可以了

不,那是行不通的。 我很确定 .NET Core 11 在最新修订的版本控制方案中紧随 .NET Core 2 之后。

@NickCraver不是var result = GetResult();误导性的表述吗? 它通常不是var result = await GetResult(); ,因此如果没有Async后缀,“这是异步的”是显而易见的吗?

@SimonCropp这正是重点:-)

您在查看代码(没有 IDE)时看到var result = GetResult();并且您不能确定它是异步的(缺少await )还是不是异步的。

var result = GetResult();是合法代码,其中result将是Task类型,而不是await ed。

如果它是var result = GetResultAsync() ,这将是对审阅者的一个很好的提示,即有问题。

@MarkOsborneMS我的观点是GetResult意味着要使用一些结果。 所以这不是一个简单的Task 。 这将是一个Task<T>并使用该结果,然后需要等待它,因此如果没有await就不会编译。

如果GetResult();仅返回Task ,并且您确实希望在它没有返回值时强制等待它,那么这意味着var result = GetResult(); (缺少等待)没有使用result ,这很容易通过将未使用的变量视为错误来捕获。

@NickCraver :虽然我同意你的观点,但我想再次指出,你的论点对于任何一段代码都是正确的:

var 结果 = GetResult()

永远不清楚,因为绝对没有类型信息可看。 所以这个例子并不特定于 Async。 如果返回类型是 Lazy<>,您会希望该方法被称为 GetResultLazy 吗? 如果它是一个 int,你会称它为 GetResultInt 吗?

所以这个例子并不特定于 Async。 如果返回类型是 Lazy<>,您会希望该方法被称为 GetResultLazy 吗? 如果它是一个 int,你会称它为 GetResultInt 吗?

这是因为 Lazy 和 Int 都没有改变流控制; 例如,可以忽略等待Task<T>的结果; 但不小心await会导致完全不同的问题并引入并发和竞争条件。

@benaadams不等待任务将返回任务本身而不是结果类型。 因此,不可能不小心将两者混淆。 您的强类型代码根本无法构建,所以我仍然不同意 Async 后缀。 而且由于后缀无论如何都只是糖衣,因此它是不可靠的。 程序员会犯错,编译器通常不会。

您的强类型代码根本无法构建,所以我仍然不同意 Async 后缀。

这完全是错误的。 让我们把我原来的例子放在一个简单的程序中:
```c#
无效的主要()
{
var 结果 = GetResult();
}

异步任务获取结果()
{
等待任务。延迟(4000);
返回 5;
}
```

这编译得很好。 它立即退出,终止正在进行的任何任务。 如果那是更新银行余额或其他东西,那将是一种耻辱...

或者让我们举其他现实生活中的例子。 假设它正在从 SQL 中获取结果。 好吧,对吧? 无论如何,我们不想要结果。 但是等等,并发使用的 SQL 连接(默认情况下 - 对于大多数应用程序而言)并不安全。 您有 2 个 TDS 流的使用者。 这会产生双方的随机结果。 因为没有等待第一个,所以两者都在非线程安全的东西上比赛和竞争。 这只是成千上万这样的例子之一。 而且我已经看到它们发生了(就像这里有很多一样),不幸的是它们远非理论上的。

@MarcoTheFirst 的意思(我认为)是以下情况:

void Method()
{
    var result = GetResult();
    int x = result + 10; // Compile error, Task<int> + int doesn't work
}

但是,我发现另一种方式更危险:

db.SaveCustomer(c);
db.SaveOrder(o);
….

@MarkusAmshove - 是的,但忽略返回值(几乎总是)是程序员错误,编译器应该为此对你大喊大叫。

然后 Roslyn 团队需要做点什么,因为这不会产生错误或警告:

static void Main(string[] 
{
    Save();

}
public static Task Save()
{
     return Task.CompletedTask;
}

....我知道它不_现在_,但在我看来它应该。

不确定它是否会导致其他类型的问题或混乱,但我认为如果默认情况下等待异步方法并且您可以选择“getTask DoSomething()”之类的东西,生活可能会更轻松。

即通过默认选择显式退出等待。

我认为在这个问题上不需要任何进一步的行动项目。 鉴于几个月来没有关于命名问题的讨论,我将结束这一点。 感谢您的精彩讨论。

此页面是否有帮助?
0 / 5 - 0 等级