在异步方法中使用“yield return”来实现流水线

Ra'*_*raj 7 c# async-await uwp

我正在构建一个 UWP 应用程序,它从文件夹中获取文件列表,对它们进行一些处理,然后删除这些文件。

这工作正常:

List<StorageFile> files;
public MainPage()
{
     this.InitializeComponent();
     files = new List<StorageFile>();
}

private async Task<List<StorageFile>> GetFiles(StorageFolder folder)
{
     var items = await folder.GetItemsAsync();          
     foreach (var item in items)
     {
         if (item.GetType() == typeof(StorageFile))
             files.Add(item);
         else
             await GetFiles(item as StorageFolder);
     }

     return files;
}


private async void GetFilesBtn_Click(object sender, RoutedEventArgs e)
{
     // opening folder picker, then selecting a folder
     var files = await GetFiles(folder);    
     // process files
     ProcessFiles(files);    
     // dispose
     DisposeFiles(files);
}
Run Code Online (Sandbox Code Playgroud)

然而,当处理大量文件时,内存消耗非常高(显然)。

因此,我想到的是使用yield return file和处理每个文件,然后一旦完成该文件,我就可以处理它,并开始处理下一个文件,依此类推。

我试图做的是:

public async Task<IEnumerable<StorageFile>> GetFiles(StorageFolder folder)
{
       var items = await folder.GetItemsAsync();    
       foreach (var item in items)
       {
            if (item.GetType() == typeof(StorageFile))
                yield return item;
            else
               await GetFiles(item as StorageFolder);
       }
}
Run Code Online (Sandbox Code Playgroud)

然后:

foreach (var file in GetFiles(folder))
{
      // process file
      ProcessFile(file);
      // dispose
      DisposeFile(file);
} 
Run Code Online (Sandbox Code Playgroud)

这样做时我得到:

“GetFiles(StorageFolder)”的主体不能是迭代器块,因为任务 IEnumerable StorageFile 不是迭代器接口类型。

我以前从未使用过yield return,所以我不确定如何实现这一点。

Cra*_*own 10

从 C# 8 开始,现在可以使用IAsyncEnumerable.

您只需将返回类型从Task<IEnumerable<StorageFile>>to更改为,然后使用而不是IAsyncEnumerable<StorageFile>调用该方法。await foreachforeach

所以你的例子现在看起来像这样:

public async IAsyncEnumerable<StorageFile> GetFiles(StorageFolder folder)
{
       var items = await folder.GetItemsAsync();    
       foreach (var item in items)
       {
            if (item.GetType() == typeof(StorageFile))
                yield return item;
            else
               await foreach (var item2 in GetFiles(item as StorageFolder))
                  yield return item2;
       }
}
Run Code Online (Sandbox Code Playgroud)

然后:

await foreach (var file in GetFiles(folder))
{
      // process file
      ProcessFile(file);
      // dispose
      DisposeFile(file);
}
Run Code Online (Sandbox Code Playgroud)


moi*_*ien -1

因为yield支持其返回类型为 的方法IEnumerable<T>GetFiles()返回Task<IEnumerable<T>>
查看以下内容:

    public IEnumerable<StorageFile> GetFiles(StorageFolder folder)
    {
        // your code here
    }

    public Task<IEnumerable<StorageFile>> GetFilesAsync(StorageFolder folder)
    {
        return Task.Run(() => GetFiles(folder));
    }
Run Code Online (Sandbox Code Playgroud)

然后你可以做
foreach (var file in GetFiles(folder))foreach (var file in await GetFilesAsync(folder))

编辑:啊,您谈论的 IAsyncEnumerable 在您发布答案的日期中不存在