1

如果我链接子句,例如

var results = elements
    .Where(n => n > 3)
    .Where(n => n % 2 == 0);

这比刚才慢吗

var results = elements.Where(n => n > 3 && n % 2 == 0);

解释为什么能或者为什么不能?

编辑:似乎共识是即使 POCO 对象也迭代两次。如果是这种情况,有人可以解释为什么微软不会组合这些谓词。我偶然发现Enumerable.CombinePredicates我认为这样做了。有人可以解释一下这是做什么的。

4

2 回答 2

2

如果您在谈论 LINQ-to-objects,每个都Where涉及设置一个新的迭代器状态机,这很昂贵,所以是的,它比将两个条件放在一起要慢。

如果您在谈论 LINQ-to-其他东西,那么,这取决于;额外的Where可能只是在某处涉及额外的字符串连接。它仍然可能更昂贵,但确切的差异取决于 LINQ 提供程序。

于 2012-12-05T14:03:28.980 回答
2

编辑:我看起来更近了一点。WhereEnumerableIterator扩展方法返回的实际Where覆盖了该Where方法并将谓词组合成一个回调。

public override IEnumerable<TSource> Where(Func<TSource, bool> predicate) {
    return new Enumerable.WhereEnumerableIterator<TSource>(
        this.source, 
        Enumerable.CombinePredicates<TSource>(this.predicate, predicate));
}

private static Func<TSource, bool> CombinePredicates<TSource>(
    Func<TSource, bool> predicate1, Func<TSource, bool> predicate2
    ) {
    return (TSource x) => predicate1(x) && predicate2(x);
}

因此,我在机器上看到的速度差异可能应该归因于其他原因。

第一个示例将遍历elements集合一次以查找满足条件的项item > 3,然后再次查找满足条件的项item % 2 == 0

第二个示例将遍历elements集合一次以查找满足条件的项目item > 3 && item % 2 == 0

在提供的示例中,第二个很可能总是比第一个快,因为它只循环elements一次。

这是我在机器(.NET 3.5)上获得的一些非常一致的结果的示例:

    var stopwatch = new System.Diagnostics.Stopwatch();
    var elements = Enumerable.Range(1, 100000000);
    var results = default(List<int>);
    stopwatch.Start();
    results = elements.Where(n => n > 3).Where(n => n % 2 == 0).ToList();
    stopwatch.Stop();
    Console.WriteLine(stopwatch.Elapsed);
    stopwatch.Reset();
    stopwatch.Start();
    results = elements.Where(n => n > 3 && n % 2 == 0).ToList();
    stopwatch.Stop();
    Console.WriteLine(stopwatch.Elapsed);
    Console.WriteLine("Done");
    Console.ReadLine();

结果:

00:00:03.0932811
00:00:02.3854886
Done

编辑
@Rawling 是正确的,因为我的解释仅适用于 POCO 对象集合中使用的 LINQ。当用作 LINQ-to-SQL、NHibernate、EF 等的接口时,您的结果将更加依赖于实现。

于 2012-12-05T14:18:02.837 回答