代码之家  ›  专栏  ›  技术社区  ›  bas

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

  •  0
  • bas  · 技术社区  · 6 年前

    我有一个linq查询,它返回一个任务对象并将其存储在 IEnumerable . 出于某种原因,select查询一直在枚举,直到任务启动或完成(我认为,这很难调试)。

    这个问题相当直截了当:

    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查询继续执行。我认为这与等待激活的任务有关,但我不知道为什么会这样。

    编辑:

    为了完整起见,我希望上述代码的工作原理与:

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

    而建筑用的是 foreach 做我所期望的事情:它用任务填充一个列表(在这个特定的例子中是1个任务的列表),这个任务执行一次。在建造中 Select

    我希望我提供的代码足够清晰,期待了解为什么select查询的行为有所不同(如果可能的话,如何避免它发生)。

    1 回复  |  直到 6 年前
        1
  •  0
  •   Kevin Avignon    6 年前

    使用“ToList”会强制迭代器遍历所有集合,即使您认为您说过“只需给我集合中的前两个项”。如果该集合有1000个元素,那么您将在该集合上迭代,直到到达最后一个项为止,它仍将为您提供2个元素。

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

    在你的方法中,你实例化一个你添加到其中的列表,你需要改进一点来使用收益率返回,因此,不需要分配不需要分配的数据。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)