C# 在lambda中具有wait的异步方法中缺少wait警告

C# 在lambda中具有wait的异步方法中缺少wait警告,c#,async-await,C#,Async Await,要么VisualStudio感到困惑,要么我(可能是我自己) 如果我有 public异步任务DoSomething() { //东西 ForEach(异步s=>await\u repo.DoThing); 返回新的响应(listofsuff.Count); } 它抱怨说 此异步方法缺少“wait” 但是,如果我将我的方法更改为 public异步任务DoSomething() { //东西 foreach(listOfStuff中的var s) { 等待回购交易; } 返回新的响应(listof

要么VisualStudio感到困惑,要么我(可能是我自己)

如果我有

public异步任务DoSomething()
{
//东西
ForEach(异步s=>await\u repo.DoThing);
返回新的响应(listofsuff.Count);
}
它抱怨说

此异步方法缺少“wait”

但是,如果我将我的方法更改为

public异步任务DoSomething()
{
//东西
foreach(listOfStuff中的var s)
{
等待回购交易;
}
返回新的响应(listofsuff.Count);
}
然后它会非常高兴,警告也就消失了


所以有两个问题,警告正确吗?如果正确,原因是什么?我的印象是这两种方法本质上是相同的,如果警告是正确的,那么我必须假设我的印象是错误的。

在第一个版本中,
DoSomething
函数缺少
wait
操作符。唯一的
wait
在传递给
ForEach
方法的异步void lambda中。在第二个版本中,
DoSomething
函数有一个
wait
,作为函数实际主体的一部分,因此您不会收到任何警告。无论
listofsuff
是否为空,都可以满足方法需要
wait
的条件

现在,功能上的差异可能还不是很明显,但它是至关重要的。如前所述,第一个版本使用异步void方法。这是不可等待的,因此该方法将在异步操作完成之前继续,正如您在本测试中看到的:

[Test]
public void DoSomething()
{
    var sw = Stopwatch.StartNew();
    var list = Enumerable.Range(0, 10).ToList();
    list.ForEach(async x => await Task.Delay(TimeSpan.FromSeconds(1)));
    Console.WriteLine($"{sw.ElapsedMilliseconds}ms");
}
结果
6ms
,而不是我们预期的10秒。代码的第二个版本是一个可适当等待的
异步任务

[Test]
public async Task DoSomething()
{
    var sw = Stopwatch.StartNew();
    var list = Enumerable.Range(0, 10).ToList();
    foreach(var x in list)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
    }
    Console.WriteLine($"{sw.ElapsedMilliseconds}ms");
}

我们看到的结果是:
10032ms
符合预期。

如果我们用本地函数替换lambda,事情可能会变得更清楚:

public async Task<Response> DoSomething()
{
    //stuff
    listOfStuff.ForEach(ProcessStuffAsync);
    return new Response(listOfStuff.Count);

    async Task ProcessStuffAsync(Stuff s) // local function
    {
        await _repo.DoThing(s);
    }
}
public异步任务DoSomething()
{
//东西
ForEach(ProcessStuffAsync);
返回新的响应(listofsuff.Count);
异步任务进程stuffAsync(Stuff s)//本地函数
{
等待回购交易;
}
}
DoSomething
方法返回一个
任务
,但它不是真正的异步,因为它缺少一个
等待
。因此,返回的
任务将处于完成状态。
DoSomething
中的所有代码(本地函数中的代码除外)将同步运行

本地函数
ProcessStuffAsync
实际上是异步的,每次调用时都会返回一个
Task
。这些任务既不是等待的,也不是存储在
DoSomething
方法中的某个地方,因此它们是触发和遗忘任务。没人知道他们会怎么样



更新:上述代码无法编译,因为无法接受
ProcessStuffAsync
本地函数作为参数。编译器抱怨其返回类型错误。要使代码编译,本地函数必须返回
void
。但是函数本身就是一罐蠕虫。

在第一个版本中,
await
是lambda函数的一部分,而不是封闭的
DoSomething
,因此
DoSomething
缺少
await
@JSteward您能详细说明一下吗?循环中的一个包含在循环中,这听起来非常类似于被封闭在lambda中。此外,如果
listofsuff
为空,则两个函数都不会实际运行,也不会触及
wait
DoSomething
是一个函数,lambda函数是。。。函数。在第一个版本中,只有lambda具有
wait
。无论
listofsuff
是否为空
DoSomething
,在第二个版本中,都有一个
wait
。好的,这两者之间有实际的功能差异吗?在迭代列表并对列表中的每个项调用方法的核心级别,这两种实现之间是否存在实际差异?采取
操作
而不是
函数
,因此当您向其发送异步函数时,您已经创建了一个不可等待的
异步void
方法。添加测试用例确实有助于澄清问题。我对
.ForEach()
ForEach
的功能差异感到非常惊讶。