在 IAsyncEnumerable 上调用 FIrst 然后 Skip(1) 可以吗?

Lor*_*eno 2 c# .net-core iasyncenumerable

我需要IAsyncEnumerable将所有结果存储在某个数据库中,但我还需要对第一项进行一些特殊处理。我是这样做的:

var someAsyncEnumerable = GetSomeAsyncEnumerable(cancellationToken);

var firstValue = await someAsyncEnumerable.FirstOrDefaultAsync(cancellationToken);

if (firstValue is not null)
{
   // log something about first value
   await HandleItem(fitstValue);
}

await foreach(var value in someAsyncEnumerable.Skip(1).WithCancellation(cancellationToken))
{
   await HandleItem(value);
}

private async Task HandleItem(Item item) 
{
   // store item in DB
}

Run Code Online (Sandbox Code Playgroud)

使用这样的代码我收到警告:

可能的多重枚举

我想知道,这只是一个在这种情况下我可以完全忽略的警告,还是我对可枚举的使用错误?

假设 myIAsyncEnumerable不应该被枚举两次,因为它效率低下,甚至会破坏。

Jon*_*eet 7

我想知道,这只是一个在这种情况下我可以完全忽略的警告,还是我对可枚举的使用错误?

“错误”是一个很强烈的词——但它肯定会引起问题。您将结果枚举两次,这在某些情况下很好,但在其他情况下却有问题。

正如你所说:

假设我的 IAsyncEnumerable 不应该被枚举两次,因为它效率低下,甚至会破坏。

...那么是的,你有问题了。因为你绝对触发枚举两次。

最好枚举一次序列,跟踪您是否正在查看第一个项目并采取相应的操作:

var someAsyncEnumerable = GetSomeAsyncEnumerable(cancellationToken);
bool first = true;
await foreach (var value in someAsyncEnumerable)
{
   if (first)
   {
       // Special handling of value
       ...
       // For subsequent loop iterations, skip this bit
       first = false;
   }
   else
   {
       await HandleItem(value);    
   }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这里我假设您想要第一个项目的完全不同的路径。如果您实际上只是想事先做额外的工作,则不需要该else块:

var someAsyncEnumerable = GetSomeAsyncEnumerable(cancellationToken);
bool first = true;
await foreach (var value in someAsyncEnumerable)
{
   if (first)
   {
       // Special handling of value
       ...
       // For subsequent loop iterations, skip this bit
       first = false;
   }
   // Regardless of whether this is the first iteration or not,
   // handle the item.
   await HandleItem(value);    
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以foreach自己将循环扩展为MoveNext调用等,但这更容易出错。

  • @mnj:不,他们绝对不这样做,而且这对他们来说也没有意义。假设您有一个“IEnumerable<int>GenerateRandomNumbers()”方法 - 如果您多次枚举它,*应该*给出不同的结果。 (2认同)