Linq Select查询返回多次枚举的任务

时间:2018-10-15 20:37:18

标签: c# task-parallel-library

我有一个linq查询,该查询返回一个任务对象并将其存储在IEnumerable中。由于某种原因,选择查询会一直枚举,直到任务开始或完成为止(我认为这很难调试)。

查询非常简单:

Context.RetrieveDataTasks = retrievableProducts.Select(product => Context.HostController.RetrieveProductDataFiles(product));

RetrieveProductDataFiles的签名是:

public Task RetrieveProductDataFiles(IProduct product)

在这种情况下,retrievableProducts是1个产品的列表:

var retrievableProducts = products
    .Where(product => AFancyButIrrelevantClause)
    .ToList();

我不介意将代码重写到foreach循环中,在该循环中我手动填写新列表来避免此问题,但我想了解为什么,select查询会继续执行。我认为这与等待激活的任务有关,但是我不知道为什么会导致这种情况。

编辑:

为了完整起见,我希望上面的代码与:: p完全相同

var retrievableDataTasks = new List<Task>();
foreach (var product in retrievableProducts)
{
    retrievableDataTasks.Add(Context.HostController.RetrieveProductDataFiles(product));
}
Context.RetrieveDataTasks = retrievableDataTasks;

使用foreach进行构造时,完全符合我的期望:在其中填充任务列表(在这种情况下为1个任务的列表),并且该任务执行一次。在使用Select查询进行构造时,一次又一次地启动了相同的1个任务。

我希望我提供的代码足够清楚,希望了解为什么选择查询的行为会有所不同(如果可能,如何避免发生这种情况)。

1 个答案:

答案 0 :(得分:0)

使用“ ToList”会强制迭代器遍历所有集合,即使您认为自己说的是“简单地给我集合中的前两项”。如果该收藏夹包含1000个元素,则将迭代该收藏夹,直到到达最后一项,它仍将为您提供2个元素。

  

您可以通过使用foreach语句或LINQ查询来使用迭代器方法。 foreach循环的每次迭代都调用iterator方法。在迭代器方法中到达yield return语句时,将返回expression,并保留代码中的当前位置。下次调用迭代器函数时,将从该位置重新开始执行。

在实例化添加到列表的方法中,您需要进行一些改进才能使用收益率,因此,不分配不需要分配的数据。 LINQ方法是延迟评估的,这意味着在尝试实现结果之前,将不会为数据分配任何内存(例如ToList)。当您使用LINQ方法时,唯一获得的内存使用是当前迭代,而不是集合中找到的所有内存。

假设使用以下代码段来帮助您。

private static IEnumerable<Product> GetMyProducts(IEnumerable<Product> products, bool AFancyButIrrelevantClause)
{
   foreach(var product in products)
   {
       if(AFancyButIrrelevantClause)
           yield return product;
    }
 }

或直接在LINQ中更简洁:

 products.Where(product => AFancyButIrrelevantClause)