c# linq 多个Where连接效果
网络整理 - 06-27
在编写LINQ时,为了保持代码清晰,我们通常会将多个Where进行连接,如
var numbers = Enumerable.Range(1, 10); var evenNumbersLessThanFive = numbers .Where(i => i % 2 == 0) .Where(i => i < 5);但多个Where是否意味着多次迭代呢?直觉告诉我们显然不是。因为Where只是生成一个可迭代的对象,在对该对象进行foreach之前,是不会真正执行迭代的。但编译器是如何处理这种Where连接的呢?
查看源代码后发现,Enumerable.Where扩展方法返回的是WhereXxxIterator<TSource>这样的类型,它们均继承自Enumerable.Iterator<TSource>类。我们以WhereEnumerableIterator<TSource>类为例,它包含一个Where方法。我们知道,当调用类的一个实例方法时,编译器就会忽略与其签名相同的扩展方法。因此在多个Where连接的代码中,从第二个Where开始,调用的就是WhereEnumerableIterator<TSource>.Where(Func<TSource, bool> predicate)方法,该方法实现如下
public override IEnumerable<TSource> Where(Func<TSource, bool> predicate) { return new Enumerable.WhereEnumerableIterator<TSource>( this.source, Enumerable.CombinePredicates<TSource>(this.predicate, predicate)); }可以看到,CombinePredicates这个方法,是将两个Func<TSource, bool>委托合并。它是一个工厂方法,生成仍然是一个Func<TSource, bool>。
private static Func<TSource, bool> CombinePredicates<TSource>( Func<TSource, bool> predicate1, Func<TSource, bool> predicate2) { <>c__DisplayClassf<TSource> classf = new <>c__DisplayClassf<TSource>(); classf.predicate1 = predicate1; classf.predicate2 = predicate2; return new Func<TSource, bool>(classf.<CombinePredicates>b__e); }其中,<>c__DisplayClassf<TSource>是一个包含两个Func<TSource, bool>属性的密封类,它的<CombinePredicates>b__e方法负责合并两个委托:
public bool <CombinePredicates>b__e(TSource x) { if (this.predicate1(x)) { return this.predicate2(x); } return false; }因此可以得出结论,多个Where连接,与将多个条件写到一个Where中,几乎是等同的,它们都只执行一次迭代。
var evenNumbersLessThanFive2 = numbers.Where(i => i % 2 == 0 && i < 5);
c# linq 多个Where连接效果