【问题标题】:Why does C# allow making an override async?为什么 C# 允许异步覆盖?
【发布时间】:2016-03-05 17:48:08
【问题描述】:

在 C# 中,当您覆盖一个方法时,允许在原始方法不是时使覆盖异步。这似乎是一种糟糕的形式。

让我想知道的问题是:我被请来协助解决负载测试问题。在大约 500 个并发用户时,登录过程将分解为重定向循环。 IIS 正在记录异常,并显示消息“异步模块或处理程序已完成,而异步操作仍处于挂起状态”。一些搜索让我认为有人在滥用async void,但我通过源快速搜索什么也没找到。

可悲的是,我正在寻找 async\s*void(正则表达式搜索),而我应该寻找更像 async\s*[^T] 的东西(假设 Task 不完全合格..你明白了)。

我后来在基本控制器中发现了async override void onActionExecuting。显然,这一定是问题所在,而且确实如此。修复该问题(暂时使其同步)解决了问题。

但它给我留下了一个问题:当调用代码永远无法等待它时,为什么你可以将覆盖标记为异步?

【问题讨论】:

  • async void 是事件处理程序的正确模式。只是不要忽视警告。
  • 不是元数据,而是programmers.SE
  • 我投票决定将此问题作为题外话结束,因为 - 基于我重新发布到 Programmers.SE 的 cmets:programmers.stackexchange.com/questions/311870/…
  • 我不认为这个问题是题外话。了解async 不是方法签名的一部分是重要的编程知识。即使它是题外话,你也不应该交叉发布它。
  • @PeterLaCombJr。 async 的重点是允许您在方法的 inside 中使用await。这并不意味着您的异步方法本身必须等待。所以不能等待异步方法不是问题

标签: c# async-await


【解决方案1】:

当基类(或接口)声明了一个返回Task的虚方法时,只要返回Task就可以覆盖它。 async 关键字只是提示编译器将您的方法转换为状态机。尽管编译器对您的方法使用了黑魔法,但编译后的方法仍然返回一个任务


至于void 虚拟方法,您可以覆盖一个没有 async 关键字(显然)并在其中启动非等待任务。当您使用 async 关键字覆盖它并在正文中使用await 时,就会发生这种情况。调用者不会等待创建的任务(因为“原始”签名是void)。两种情况相似*:

public override void MyVirtualMethod()
{
    // Will create a non awaited Task (explicitly)
    Task.Factory.StartNew(()=> SomeTaskMethod());  
}

public override async void MyVirtualMethod()
{
    // Will create a non awaited Task (the caller cannot await void)
    await SomeTaskMethod();  
}

Stephen Cleary's article 对此有一些注释:

  • 返回无效的异步方法有一个特定目的:使异步事件处理程序成为可能。
  • 你应该更喜欢 async Task 而不是 async void。

*SomeTaskMethod 的实现、底层框架、SynchronizationContext 和其他因素可能也将导致上述每个方法的不同结果。

【讨论】:

  • 这个 - 这是主题。在这种情况下(任务返回方法),异步的覆盖可能是有意义的。但我认为不是为了无效。
  • @PeterLaCombJr。哦,我错过了void 部分:) 请参阅我的附加段落。
【解决方案2】:

您可以覆盖async 方法,因为异步不是方法签名的一部分。实际上,异步允许通过在内部创建状态机在您的方法中使用 await 关键字。
你可以在这里找到更多关于异步的信息:http://blog.sublogic.com/2012/05/14/async-isnt-really-part-of-your-method-signature/

【讨论】:

  • 虽然链接的文章很有趣,但它没有说明为什么不将异步作为签名的一部分。
  • @PeterLaCombJr.async Task Foo()Task Foo() 重载是没有意义的。
  • True - 但你也不能通过返回类型来区分方法。
  • @PeterLaCombJr。关键是async 关键字存在的唯一原因是为了赋予await 其特殊含义,这样使用名为“await”的变量的旧代码仍然可以编译。否则添加 async 不会改变函数。
  • 答案中的链接已失效 - "403 Forbidden".
【解决方案3】:

async 不是“合同”的一部分。不幸的是,在我看来,这是一个实现细节出现在错误的地方。

将返回Task 的(非async)方法更改为async (或反之亦然)是完全合法的,这不会造成重大更改,也不需要调用者重新编译。

进一步表明它不属于合同的一部分是,您不允许在接口中将函数标记为async,并且在这里,完全可以用非async 覆盖async,反之亦然.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-03
    • 2013-02-21
    • 2011-02-27
    • 2012-05-08
    • 1970-01-01
    • 1970-01-01
    • 2018-09-24
    相关资源
    最近更新 更多