Where子句的调用次数多于项目次数

时间:2014-04-05 17:30:04

标签: c# linq linq-to-objects

我有一个List<Order>,我试图使用LINQ过滤这个:

var grouped = from o in orders
  group o by o.OrderNumber into g
  select new { Id = g.Key, Orders = g };

var GroupedList = grouped.ToList();

int max = GroupedList.Count();
int count = 0;
var filtered =
    from g in GroupedList
    where IncrementProgress(max, ref count)
    select g.Id;

var filteredOrders = orders.Where(o => filtered.Contains(o.OrderNumber));

IncrementProgress内我打印countmax到调试输出。 max在我的测试3500中,我从1500 count获得输出并计数。

有人知道为什么吗?

PS:在我的生产代码中,有过滤逻辑而不是IncrementProgress

更新

这里是IncrementProgress - 方法:

private bool IncrementProgress(int max, ref int count)
{
    Debug.WriteLine("Filtering {0} of {1}", ++count, max);
    return true;
}

2 个答案:

答案 0 :(得分:2)

每次枚举过滤后的集合时都会执行LINQ查询,在每次调用Contains方法的情况下都会执行。

尝试将过滤后的变量声明为(<LINQ Query>).ToArray()。 这将只查询一次查询。

抱歉格式不佳(手机)。 希望它有所帮助。

答案 1 :(得分:2)

那是因为LINQ是惰性的而filtered不是集合 - 它是一个内存中的查询,它只存储如何评估结果的信息,而不是结果本身。因此,每次使用filtered时,都会再次对其进行评估,重复GroupedList并再次检查where条件。

这意味着,where条件将被评估orders.Count() * GroupedList.Count()次。

ToList()添加filtered来急切地评估它。

var filtered =
    (from g in GroupedList
     where IncrementProgress(max, ref count)
     select g.Id).ToList();

但是,由于您之后仅在Contains上使用filtered,因此您应使用HashSet<int>来存储结果。这将使Contains调用 O(1)而不是 O(n),这将大大提高性能。

var filtered =
    new HashSet<int>(from g in GroupedList
                     where IncrementProgress(max, ref count)
                     select g.Id);