如何在异步 C# 任务中使用 yield

How to use yield in async C# task

我正在尝试使用 yield 和 return 在异步任务中将 X 转换为 Y 的结果。但是,我在 select 上收到错误消息。 错误是:

Error CS1942 The type of the expression in the select clause is incorrect. Type inference failed in the call to 'Select'.

public async Task<Result<dynamic>> GetYAsync(IEnumerable<X> infos)
    {
        return Task.WhenAll(from info in infos.ToArray() select async ()=>
        {
            yield return await new Y(info.Id, "Start");
        });
    }

你没有。异步枚举支持(并且 yield 用于实现可枚举)在 2019 年的某个时候随 C# 8 一起出现。所以,现在的答案很简单,你不会。

你得到错误的原因是你也不能return一个结果。 Yield (return) 特定于实现枚举。您的方法签名不匹配。

简答:您不能使用异步 yield 语句。

但在大多数情况下,您不需要这样做。使用 LINQ 您可以在将所有任务传递到 Task.WaitAll 之前聚合所有任务。我将您的示例简化为 return 和 IEnumerable<int>,但这适用于所有类型。

public class Program
{
   public static Task<int> X(int x) 
   {
       return Task.FromResult(x);
   }
    
   public static async Task<IEnumerable<int>> GetYAsync(IEnumerable<int> infos)
   {
       var res = await Task.WhenAll(infos.Select(info => X(info)));
       return res;
   }
    
   public static async void Main()
   {
       var test = await GetYAsync(new [] {1, 2, 3});
       Console.WriteLine(test);
   }
}

您的示例还有一个错误await new Y(...),构造函数不能是异步的,因此我将其替换为异步函数。 (正如评论中所暗示的,在技术上可以创建自定义等待类型并使用 new 创建此类型,尽管很少使用),

上面的示例使用 infos.Select 创建待处理任务列表,return 通过调用函数 X 编辑。然后将等待并return编辑此任务列表。

这个workaround应该适合大多数情况。 .Net 不支持真正的异步迭代器,例如 JavaScript。

更新:此功能当前建议作为语言提案:Async Streams。所以也许我们将来会看到这个。

更新:如果您需要异步迭代器,目前有几个选项可用:

  1. Reactive Stream,RX.Net,它为您提供基于事件的异步可观察流。
  2. 有异步迭代器或异步枚举的实现AsyncEnumerable.Net Async Enumerable