【问题标题】:Alternative to yielding in an IAsyncEnumerable在 IAsyncEnumerable 中让步的替代方法
【发布时间】:2019-11-16 11:15:09
【问题描述】:

我正在尝试检索使用 C# 8.0 中的新 AsyncEnumerables 进行分页的数据。回到同步 IEnumerable 世界,代码看起来像这样:

private IEnumerable<S3Object> Example(S3FilesRequest requestData)
    {
        var request = new ListObjectsV2Request()
        {
            BucketName = requestData.Bucket,
            Prefix = requestData.KeyPrefix
        };

        ListObjectsV2Response response;
        do
        {
            response = this.client.ListObjectsV2Async(request).Result;
            foreach (var s3Obj in response.S3Objects)
                yield return s3Obj;
            request.ContinuationToken = response.NextContinuationToken;
        } while (response.IsTruncated);
    }
    // Not tested - can probably be more succinct (using TakeWhile and SelectMany) but you get the idea

使用 IAsyncEnumerable 我认为它看起来像这样:

private IAsyncEnumerable<S3Object> listObjects(S3FilesRequest requestData)
        => AsyncEnumerable.CreateEnumerable(() =>
        {
            var request = new ListObjectsV2Request()
            {
                BucketName = requestData.Bucket,
                Prefix = requestData.KeyPrefix
            };
            ListObjectsV2Response response = null;
            List<S3Object>.Enumerator enumerator = default;
            S3Object current = null;
            bool movedNext = false;

            return AsyncEnumerable.CreateEnumerator<S3Object>(async c =>
            {
                if (response == null || !movedNext)
                {
                    enumerator.Dispose();
                    response = await this.client.ListObjectsV2Async(request, c);
                    enumerator = response.S3Objects.GetEnumerator();
                }

                movedNext = enumerator.MoveNext();
                current = enumerator.Current;

                request.ContinuationToken = response.NextContinuationToken;
                return movedNext || response.IsTruncated;
            }, () => current, enumerator.Dispose);
        });

这似乎有点啰嗦,但是没有yield关键字还有更好的方法吗?

谢谢,

编辑:

这是由 ReSharper 在升级到 C# 8 时静默安装包 System.Interactive.Async 引起的。我将其卸载并遵循 Stephen 的建议。

对于谷歌:

CS0518  Predefined type 'System.Runtime.CompilerServices.AsyncIteratorMethodBuilder' is not defined or imported
CS0656  Missing compiler required member 'System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Create'
CS0656  Missing compiler required member 'System.Collections.Generic.IAsyncEnumerable`1.GetAsyncEnumerator'
CS0656  Missing compiler required member 'System.Collections.Generic.IAsyncEnumerator`1.MoveNextAsync'
CS0656  Missing compiler required member 'System.IAsyncDisposable.DisposeAsync'
CS0656  Missing compiler required member 'System.Threading.Tasks.ValueTask`1..ctor' 
CS0656  Missing compiler required member 'System.Threading.Tasks.ValueTask..ctor'
CS0656  Missing compiler required member 'System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore`1.GetResult'cs
CS0656  Missing compiler required member 'System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore`1.GetStatus'cs
CS0656  Missing compiler required member 'System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore`1.get_Version'cs 
CS0656  Missing compiler required member 'System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore`1.OnCompleted'cs
CS0656  Missing compiler required member 'System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore`1.Reset' 
CS0656  Missing compiler required member 'System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore`1.SetExceptioncs 
CS0656  Missing compiler required member 'System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore`1.SetResult'cs 
CS0656  Missing compiler required member 'System.Threading.Tasks.Sources.IValueTaskSource`1.GetResult' 
CS0656  Missing compiler required member 'System.Threading.Tasks.Sources.IValueTaskSource`1.GetStatus' 
CS0656  Missing compiler required member 'System.Threading.Tasks.Sources.IValueTaskSource`1.OnCompleted'
CS0656  Missing compiler required member 'System.Threading.Tasks.Sources.IValueTaskSource.GetResult' 
CS0656  Missing compiler required member 'System.Threading.Tasks.Sources.IValueTaskSource.GetStatus' 
CS0656  Missing compiler required member 'System.Threading.Tasks.Sources.IValueTaskSource.OnCompleted' 

【问题讨论】:

  • 顺便问一下,为什么这个问题被否决了?

标签: c# asynchronous ienumerable c#-8.0 iasyncenumerable


【解决方案1】:

如果您使用的是 C# 8.0,那么您可以使用 yieldasync 就好了:

private async IAsyncEnumerable<S3Object> Example(S3FilesRequest requestData)
{
  var request = new ListObjectsV2Request()
  {
    BucketName = requestData.Bucket,
    Prefix = requestData.KeyPrefix
  };

  ListObjectsV2Response response;
  do
  {
    response = await this.client.ListObjectsV2Async(request);
    foreach (var s3Obj in response.S3Objects)
      yield return s3Obj;
    request.ContinuationToken = response.NextContinuationToken;
  } while (response.IsTruncated);
}

(以及 TakeWhileSelectMany 等异步 LINQ 方法在 System.Linq.Async package 中)

【讨论】:

  • 谢谢斯蒂芬,这个包将非常有用。然而上面的代码基本上是我最初写的。如果我尝试使用它,尽管我在 VS 中没有得到任何红色下划线,但我得到很多这样的构建错误:CS0656 Missing compiler required member System.Threading.Tasks.ValueTask1..ctor'CS0518 Predefined type 'System.Runtime.CompilerServices.AsyncIteratorMethodBuilder' is not defined or imported 等。这让我认为我可能在升级到的过程中破坏了我的项目C# 8 或者我缺少一些核心包,我会进一步调查。
  • 解决了,当我使用 IAsyncEnumerable ReSharper 提供的升级我的语言版本时,它还默默地安装了包 System.Interactive.Async。我已经卸载它并安装了 System.Linq.Async,现在一切正常。感谢您的帮助。
猜你喜欢
  • 2020-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-04
  • 2021-05-01
  • 1970-01-01
  • 2021-12-29
  • 2020-03-04
相关资源
最近更新 更多