C# 为什么';包含异步lambda的方法本身是否需要异步?

C# 为什么';包含异步lambda的方法本身是否需要异步?,c#,asynchronous,lambda,c#-5.0,C#,Asynchronous,Lambda,C# 5.0,例如,每当我等待某些内容时,编译器就会通知我包含的方法必须是异步的。为什么异步lamba不是这样看的?如果ForEach正在隐藏它,那么不返回任务对象和ForEach隐式异步void是否存在一些危险 public void SaveSome() { Array.ForEach(Enumerable.Range(0,3).ToArray(), async x => await SaveRep()); } 异步lambda只是创建异步委托的一种简单方法。没有什么可以说,包含它的方法

例如,每当我等待某些内容时,编译器就会通知我包含的方法必须是异步的。为什么异步lamba不是这样看的?如果ForEach正在隐藏它,那么不返回任务对象和ForEach隐式异步void是否存在一些危险

public void SaveSome()
{
    Array.ForEach(Enumerable.Range(0,3).ToArray(), async x =>  await SaveRep());
}

异步lambda只是创建异步委托的一种简单方法。没有什么可以说,包含它的方法本身必须做任何异步的事情——lambda表达式中的任何
await
表达式都不会使包含它的方法等待(当然,除非它正在等待一个碰巧依赖于委托的任务)

基本上,lambda表达式是在表达一些异步代码-它不是在执行异步代码本身。。。因此,包含方法不一定是异步执行的

是的,您给出的示例是对异步lambdas的误用-但是将方法设为异步并不能改善问题,而且这只是误导

编辑:作为一种可供选择的思维方式,考虑一下对原始代码的重构:

public void SaveSome()
{
    Action<int> action = SaveRepAsync;
    Array.ForEach(Enumerable.Range(0,3).ToArray(), action);
}

private static async void SaveRepAsync(int x)
{
    await SaveRep();
}
public void SaveSome()
{
Action=SaveRepAsync;
ForEach(可枚举的.Range(0,3).ToArray(),action);
}
专用静态异步void SaveRepAsync(int x)
{
等待SaveRep();
}

SaveSome
方法没有任何异步功能-只有
SaveRepAsync
方法有。。。这就是需要
async
修饰符的地方。这实际上只是对代码的一个小小的重构(编译器可以有效地进行重构)。如果您希望包含异步lambda的每个方法都有异步修饰符,这就像在上面的代码中说,
SaveSome
也应该有修饰符一样。。。这是没有意义的,我想。

你只能在
async
方法中等待
async
方法,但是你仍然可以在非异步方法中调用它们,就像你在上面做的那样-在这种情况下,它更像是一个“开火然后忘记”的方法

如果您能再解释一下为什么上述内容在生产代码中是个坏主意,我将不胜感激。我当前的异步心智模型要求:1)所有异步调用都是从返回类型为Task或Task的异步方法中调用的;2)在调用堆栈中一直执行此操作,就像抛出异常一样任务将保存异常3)只有事件处理程序应为async void。所以这是一种合法但不好的语言,为什么?我说的对吗?@Jayce:你调用的是
Array.ForEach
,它通常会以串联方式进行操作,但你的异步lambda只是在等待其他东西。实际上,你只是在做一个“失火即忘”的操作,但没有任何迹象表明什么时候一切都结束了。如果你真的想要一个完全“火”而“忘”的方法,我个人会选择一种不同的表达方式。就
Array.ForEach
而言,它只是被赋予了一个
操作
@jayce:一个更安全的解决方案是
wait Task.whalll(Enumerable.Range(0,3)。选择(x=>SaveRep())
,它将每个元素投影到
任务中,然后等待所有元素。这样就避免了
async void
@StephenCleary:当然,这改变了意义——在所有保存完成之前,这不会完成。目前我们还不知道OP想要实现什么,这让我们很难提供帮助。@JonSkeet感谢他们,这只是我好奇的一些单元测试代码。我最初有一个for循环,然后CodeRush给了我一个选项,使它成为一个1行程序。我想我的指导方针是可以接受的。