为什么C#允许覆盖异步?

Pet*_*Jr. 29 c# async-await

在C#中,当覆盖方法时,允许在原始方法不是时使覆盖异步.这似乎很糟糕.

令我惊讶的问题是:我被引入以协助负载测试问题.在大约500个并发用户中,登录过程将分解为重定向循环.IIS正在使用消息"在异步操作仍处于挂起状态时完成异步模块或处理程序"来记录异常.一些搜索让我觉得有人在滥用async void,但我对源头的快速搜索一无所获.

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

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

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

sha*_*y__ 27

当基类(或接口)声明一个返回Task的虚方法时,只要返回Task,就可以覆盖它.该async关键字仅仅是一个暗示,编译器将您的方法进入状态机.虽然编译器在你的方法上做了它的黑魔法,但编译的方法仍然返回一个Task.


至于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();  
}
Run Code Online (Sandbox Code Playgroud)

Stephen Cleary的文章对此有一些注意事项:

  • 返回异常的异步方法有一个特定的目的:使异步事件处理程序成为可能.
  • 您应该更喜欢异步任务到异步void.

*SomeTaskMethod底层框架,SynchronizationContext和其他因素的实现可能会并将导致上述每种方法的不同结果.


Kir*_*ill 11

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

  • @ PeterLaCombJr.it让`async Task Foo()`和`Task Foo()`重载是没有意义的. (3认同)
  • @PeterLaCombJr。关键是`async`关键字存在的_唯一原因_是赋予`await`它特殊的含义,以便使用名为“await”的变量的旧代码仍然可以编译。否则添加 `async` 不会改变函数。 (2认同)

Dam*_*ver 6

async不属于"合同"的一部分.这是一个实现细节,在我看来,不幸的是,它出现在错误的地方.

它是完全合法的改变(非async)方法返回Taskasync没有,作为一个重大更改一个(或反之亦然),并且不要求呼叫者重新编译.

进一步表明它不是合同的一部分是你不允许async在接口内标记功能,因为这里完全可以async用非覆盖,async反之亦然.