Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/334.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 我可以等待使用生成器创建的可枚举项吗?_C#_Asynchronous_Async Await_Generator_Promise - Fatal编程技术网

C# 我可以等待使用生成器创建的可枚举项吗?

C# 我可以等待使用生成器创建的可枚举项吗?,c#,asynchronous,async-await,generator,promise,C#,Asynchronous,Async Await,Generator,Promise,假设我有一个异步获取的整数序列 async Task<int> GetI(int i){ return await Task.Delay(1000).ContinueWith(x => i); } 异步任务GetI(inti){ 返回等待任务。延迟(1000)。继续(x=>i); } 我想在该序列上创建一个生成器,如果序列是同步的,我会: IEnumerable<int> Method() { for (var i = 0; i < 100

假设我有一个异步获取的整数序列

async Task<int> GetI(int i){
    return await Task.Delay(1000).ContinueWith(x => i);
}
异步任务GetI(inti){ 返回等待任务。延迟(1000)。继续(x=>i); } 我想在该序列上创建一个生成器,如果序列是同步的,我会:

IEnumerable<int> Method()
{
    for (var i = 0; i < 100; i++)
    {
        yield return GetI(i); // won't work, since getI returns a task
    }
}
IEnumerable方法()
{
对于(变量i=0;i<100;i++)
{
yield return GetI(i);//不起作用,因为GetI返回一个任务
}
}
因此,我认为类比是使生成器异步并从中产生:

async Task<IEnumerable<int>> Method()    
{
    for (var i = 0; i < 100; i++)
    {
        yield return await Task.Delay(1000).ContinueWith(x => i);
    }
}
异步任务方法()
{
对于(变量i=0;i<100;i++)
{
收益返回等待任务。延迟(1000)。继续(x=>i);
}
}
这是行不通的,因为具有
yield
的方法必须返回某个对象的
IEnumerable
,另一个更有意义的方法是
IEnumerable
,但它不会编译,因为
async
方法必须返回
Task
s或void

现在,我意识到我可以简单地删除wait并返回一个
IEnumerable
,但这对我没有帮助,因为迭代会在任何数据准备就绪之前不断请求数据,所以它不能解决我的问题

  • 有什么方法可以很好地将枚举数和任务与语言给我的等待和产出的美妙糖分结合起来呢
  • 有什么方法可以很好地消费它吗

(通过在线搜索,我怀疑第一个问题的答案是错误的,第二个是观察者/可观察者,但我找不到任何规范参考,我对在C#中实现此模式的最佳方式感兴趣)

您可以返回一个
IEnumerable


异步序列很有趣。有许多不同的方法,具体取决于您想要做什么。我不完全清楚您想要的语义,所以以下是一些选项

任务
是一个异步检索的集合。只有一个任务(一个异步操作)检索整个集合。这听起来不像是你想要的

IEnumerable
是(异步)数据的(同步)序列。有多个任务,可能同时处理,也可能不同时处理。有几个选项可以实现这一点。一种是使用枚举器块并生成任务;这种方法将在每次从可枚举项检索下一项时启动新的异步操作。或者,您可以创建并返回所有任务同时运行的任务集合(这可以通过LINQ的
Select
ToList
/
ToArray
在源序列上优雅地完成)。但是,这有两个缺点:无法异步确定序列是否已经结束,并且在返回当前项(这是通常需要的行为)后立即开始下一项处理也不容易

核心问题是
IEnumerable
本质上是同步的。有两种变通方法。一种是
IAsyncEnumerable
,它是
IEnumerable
的异步等价物,在中可用。不过,这种方法也有其自身的缺点。当然,您将失去对
IEnumerable
(即枚举器块和
foreach
)的良好语言支持。而且,“异步可枚举”的概念本身并不是完全有效的;理想情况下,异步API应该是粗块的,而不是健谈的,并且可枚举项非常健谈。更多关于和的讨论


因此,现在一个更常见的解决方案是使用or(两者都可以通过NuGet获得)。在这些情况下,你必须把“序列”看作是有自己生命的东西。观测值是基于推送的,因此消费代码(理想情况下)是被动的。数据流有参与者的感觉,因此它们的行为更加独立,再次将结果推送到消费代码。

您实际上是在尝试获取
任务
,还是
IEnumerable
?后者更容易实现。。。但是你到底想从中得到什么还不清楚。(顺便说一句,我知道在这种情况下表达你想要达到的目标可能非常困难。)@JonSkeet听起来很愚蠢,我想我是想得到一个
任务,我想得到一个迭代器,我将首先等待它,然后在每次迭代中等待它。然而,
IEnumerable
也是可以接受的,问题是,正如我在问题中提到的,IEnumerable将在消费者上同步消费,我希望在每次迭代中等待它,然后再消费下一个元素。我想知道是否有一种聪明的方法可以做到这一点,它不需要通过
wait
在那里进行常规的for循环来暂停迭代。@BenjaminGruenbaum与此密切相关。另外,您正在尝试的是一个很棒的C#language+LINQ运行时功能,但它还不可用。还可以查看Stephen Toub的。您要查找的是类似于
IAsyncEnumerable
的内容,它公开了每个元素的异步。请注意,使用者不需要等待任务完成后再请求下一个任务,因此,如果生成器的构建不能在并行请求多个项目时正常工作,则可能会出现问题。
IEnumerable<Task<int>> Method()
{
    for (var i = 0; i < 100; i++)
    {
        yield return Task.Delay(1000).ContinueWith(x => i);
    }
}
foreach(var i in Method())
    Console.WriteLine(await i);