C# Linq源码分析之Where

概要

在Dotnet 开发过程中,无论是Web还是Form或是其他领域的开发,Where作为IEnumerable的扩展方法,十分常用。本文对Where方法的关键源码进行简要分析,以方便大家日后更好的使用该方法。

本文分析的源码来自 https://github.com/dotnet/runtime.git

代码结构分析

Where方法的过滤功能主要通过迭代器实现,其源代码包含7个迭代器。按照功能划分如下:

  1. 索引参与过滤操作运算的迭代器WhereIterator,容器包含Enmuerable,List和Array。
  2. 索引不参与过滤运算的
    1. Enmuerable迭代器:WhereEnumerableIterator 和WhereSelectEnumerableIterator
    2. List迭代器: WhereListIterator 和WhereSelectListIterator
    3. Array迭代器 WhereArrayIterator 和WhereSelectArrayIterator

Where源码结构及相关辅助类

类名或方法名 代码位置 基本描述
public static IEnumerable Where(this IEnumerable source, Func predicate) Where.cs 对外提供的扩展方法,参数是一个返回值为bool类型的委托。
public static IEnumerable Where(this IEnumerable source, Func predicate) Where.cs 对外提供的扩展方法,参数是一个返回值为bool类型的委托,集合中每项的索引参与委托运算。
private static IEnumerable WhereIterator Where.cs 迭代器,仅用于带索引的Where扩展方法
private sealed partial class WhereEnumerableIterator : Iterator Where.cs 迭代器,用于Enumerable做为容器的数据迭代
internal sealed partial class WhereArrayIterator : Iterator Where.cs 迭代器,用于Array做为容器的数据迭代
private sealed partial class WhereListIterator : Iterator Where.cs 迭代器,用于List做为容器的数据迭代
private sealed partial class WhereSelectArrayIterator : Iterator Where.cs 迭代器,用于Array容器中的Where().Select()的情况
private sealed partial class WhereSelectListIterator : Iterator Where.cs 迭代器,用于List容器中的Where().Select()的情况
private sealed partial class WhereSelectEnumerableIterator : Iterator Where.cs 迭代器,用于Enumerable容器中的Where().Select()的情况
internal static class ThrowHelper ThrowHelper.cs 所有Linq方法的异常管理类
internal abstract class Iterator : IEnumerable, IEnumerator Iterator.cs 迭代器的基类
public static Func CombinePredicates(Func predicate1, Func predicate2) Utilities.cs 如果有多个Where语句,将每个Where的过滤条件按照And方式合并
public static Func CombineSelectors(Func selector1, Func selector2) Utilities.cs 如果Where语句后面有多个Select,将每个Select中的Selector进行合并

关键代码分析

Enmuerable迭代器, List迭代器和 Array迭代器在代码实现上相差不大,都是通过设计模式中的迭代器模式实现具体的功能。区别是Enmuerable迭代器, List迭代器都是调用容器自身的迭代器实现逐个元素的比较和过滤,而Array迭代器是通过数组索引操作实现元素比较和过滤。

基类迭代器 Iterator源码分析

Iterator类是WhereEnumerableIterator , WhereSelectEnumerableIterator, WhereListIterator , WhereSelectListIterator, WhereArrayIterator 和WhereSelectArrayIterator的基类。

using System.Collections;
using System.Collections.Generic;

namespace System.Linq
{
    public static partial class Enumerable
    {
        /// 
        /// A base class for enumerables that are loaded on-demand.
        /// 
        /// The type of each item to yield.
        /// 
        /// 
        /// 
        /// The value of an iterator is immutable; the operation it represents cannot be changed.
        /// 
        /// 
        /// However, an iterator also serves as its own enumerator, so the state of an iterator
        /// may change as it is being enumerated.
        /// 
        /// 
        /// Hence, state that is relevant to an iterator's value should be kept in readonly fields.
        /// State that is relevant to an iterator's enumeration (such as the currently yielded item)
        /// should be kept in non-readonly fields.
        /// 
        /// 
        /// 
        internal abstract class Iterator<TSource> : IEnumerable<TSource>, IEnumerator<TSource>
        {
            private readonly int _threadId;
            internal int _state;
            internal TSource _current = default!;

            /// 
            /// Initializes a new instance of the  class.
            /// 
            protected Iterator()
            {
                _threadId = Environment.CurrentManagedThreadId;
            }

            /// 
            /// The item currently yielded by this iterator.
            /// 
            public TSource Current => _current;

            /// 
            /// Makes a shallow copy of this iterator.
            /// 
            /// 
            /// This method is called if  is called more than once.
            /// 
            public abstract Iterator<TSource> Clone();

            /// 
            /// Puts this iterator in a state whereby no further enumeration will take place.
            /// 
            /// 
            /// Derived classes should override this method if necessary to clean up any
            /// mutable state they hold onto (for example, calling Dispose on other enumerators).
            /// 
            public virtual void Dispose()
            {
                _current = default!;
                _state = -1;
            }

            /// 
            /// Gets the enumerator used to yield values from this iterator.
            /// 
            /// 
            /// If  is called for the first time on the same thread
            /// that created this iterator, the result will be this iterator. Otherwise, the result
            /// will be a shallow copy of this iterator.
            /// 
            public IEnumerator<TSource> GetEnumerator()
            {
                Iterator<TSource> enumerator = _state == 0 && _threadId == Environment.CurrentManagedThreadId ? this : Clone();
                enumerator._state = 1;
                return enumerator;
            }

            /// 
            /// Retrieves the next item in this iterator and yields it via .
            /// 
            /// true if there was another value to be yielded; otherwise, false.
            public abstract bool MoveNext();

            /// 
            /// Returns an enumerable that maps each item in this iterator based on a selector.
            /// 
            /// The type of the mapped items.
            /// The selector used to map each item.
            public virtual IEnumerable<TResult> Select<TResult>(Func<TSource, TResult> selector)
            {
                return new SelectEnumerableIterator<TSource, TResult>(this, selector);
            }

            /// 
            /// Returns an enumerable that filters each item in this iterator based on a predicate.
            /// 
            /// The predicate used to filter each item.
            public virtual IEnumerable<TSource> Where(Func<TSource, bool> predicate)
            {
                return new WhereEnumerableIterator<TSource>(this, predicate);
            }

            object? IEnumerator.Current => Current;

            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

            void IEnumerator.Reset() => ThrowHelper.ThrowNotSupportedException();
        }
    }
}

基类迭代器最核心的操作是定义迭代器与线程的关系,并且定义迭代器的相关操作。

  1. 在构造方法中获取当前线程的Id。
  2. 该类实现了接口IEnumerable,所以要实现方法GetEnumerator。如果当前迭代器的线程Id和当前线程的Id不同,则克隆一个新的迭代器返回,否则返回当前迭代器。
  3. 将迭代器初始状态设置为1,并返回当前迭代器。
  4. 该类实现了接口IEnumerator,所以要实现方法MoveNext, Reset和Current 属性。MoveNext方法根据具体容器不同,交给具体容器去实现,只是通过抽象方法做一个名义上的实现。
  5. Clone方法为虚方法,由派生类更加具体的迭代器返回指定的迭代器实例。
  6. Where方法为虚方法,默认返回WhereEnumerableIterator迭代器。
  7. Select方法为虚方法,默认返回WhereSelectEnumerableIterator迭代器。
  8. Where和Select两个虚方法主要用于具体容器迭代器的复用或复写。如果调用Where的迭代器是一个是WhereEnumerableIterator 或 WhereSelectEnumerableIterator或 WhereListIterator 或 WhereSelectListIterator或 WhereArrayIterator 或 WhereSelectArrayIterator中的对象,则会覆盖其中一个或两个方法,如果是是其它迭代器,例如Distinct迭代器,则会使用基类的Where和Select方法。

Where 扩展方法1

 public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
        {
            if (source == null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
            }

            if (predicate == null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.predicate);
            }

            if (source is Iterator<TSource> iterator)
            {
                return iterator.Where(predicate);
            }

            if (source is TSource[] array)
            {
                return array.Length == 0 ?
                    Empty<TSource>() :
                    new WhereArrayIterator<TSource>(array, predicate);
            }

            if (source is List<TSource> list)
            {
                return new WhereListIterator<TSource>(list, predicate);
            }

            return new WhereEnumerableIterator<TSource>(source, predicate);
        }

  1. 检查source容器对象不能为空,如果为空,抛出异常
  2. 检查过滤器委托不能为空,如果为空,抛出异常。如果Where()中没有任何条件,也就没有意义了。
  3. 如果参数source为一个迭代器,则分两种情况:
    1. 如果是Where相关的迭代器,例如XX.Where().Where()调用。此处(iterator.Where(predicate))的Iterator是Where相关的迭代器对象,例如下节的WhereListIterator, Where方法则是派生类WhereXXXIterator复写的Where方法返回的也是WhereXXXIterator对象,XXX表示List或Array或Enumerable。
    2. 如果是其迭代器,例如XX.Distinct().Where()调用。此处(iterator.Where(predicate))的Iterator是Distinct的迭代器对象,Where方法则是基类Iterator的Where方法,返回是默认的WhereEnumerableIterator对象。
  4. 如果source是一个数组并且长度不为0, 返回WhereArrayIterator实例。
  5. 如果source是一个List, 返回WhereListIterator实例。
  6. 返回默认的WhereEnumerableIterator实例。

迭代器 WhereListIterator源码分析

/// 
        /// An iterator that filters each item of a .
        /// 
        /// The type of the source list.
        private sealed partial class WhereListIterator<TSource> : Iterator<TSource>
        {
            private readonly List<TSource> _source;
            private readonly Func<TSource, bool> _predicate;
            private List<TSource>.Enumerator _enumerator;

            public WhereListIterator(List<TSource> source, Func<TSource, bool> predicate)
            {
                Debug.Assert(source != null);
                Debug.Assert(predicate != null);
                _source = source;
                _predicate = predicate;
            }

            public override Iterator<TSource> Clone() =>
                new WhereListIterator<TSource>(_source, _predicate);

            public override bool MoveNext()
            {
                switch (_state)
                {
                    case 1:
                        _enumerator = _source.GetEnumerator();
                        _state = 2;
                        goto case 2;
                    case 2:
                        while (_enumerator.MoveNext())
                        {
                            TSource item = _enumerator.Current;
                            if (_predicate(item))
                            {
                                _current = item;
                                return true;
                            }
                        }

                        Dispose();
                        break;
                }

                return false;
            }

            public override IEnumerable<TResult> Select<TResult>(Func<TSource, TResult> selector) =>
                new WhereSelectListIterator<TSource, TResult>(_source, _predicate, selector);

            public override IEnumerable<TSource> Where(Func<TSource, bool> predicate) =>
                new WhereListIterator<TSource>(_source, CombinePredicates(_predicate, predicate));
        }
  1. WhereListIterator继承自Iterator。
  2. 在构造器中获取List对象和过滤条件的泛型参数。
  3. 覆盖Clone方法,对于WhereListIterator,克隆方法返回WhereListIterator实例。
  4. 按照List的结构,覆盖MoveNext()方法:
    1. 获取List的迭代器。
    2. Iterator类中已经定义了_state初始值是1,修改为2后,进入case 2。
    3. 使用List迭代器获取List内元素。
    4. 如果满足过滤条件,则返回true,否则返回false。
    5. 释放当前迭代器。
  5. 为应对Where().Select()的调用方式,复写Select方法,返回WhereSelectListIterator实例。
  6. 为应对Where().Where()的调用方式,复写Where方法,返回新的WhereListIterator实例,但是两个Where的过滤条件按照And方式进行合并,合并代码请参看附录。

迭代器 WhereSelectListIterator源码分析

该迭代器将Where和Select两个操作合并,通过一个迭代器实现。

/// 
        /// An iterator that filters, then maps, each item of a .
        /// 
        /// The type of the source list.
        /// The type of the mapped items.
        private sealed partial class WhereSelectListIterator<TSource, TResult> : Iterator<TResult>
        {
            private readonly List<TSource> _source;
            private readonly Func<TSource, bool> _predicate;
            private readonly Func<TSource, TResult> _selector;
            private List<TSource>.Enumerator _enumerator;

            public WhereSelectListIterator(List<TSource> source, Func<TSource, bool> predicate, Func<TSource, TResult> selector)
            {
                Debug.Assert(source != null);
                Debug.Assert(predicate != null);
                Debug.Assert(selector != null);
                _source = source;
                _predicate = predicate;
                _selector = selector;
            }

            public override Iterator<TResult> Clone() =>
                new WhereSelectListIterator<TSource, TResult>(_source, _predicate, _selector);

            public override bool MoveNext()
            {
                switch (_state)
                {
                    case 1:
                        _enumerator = _source.GetEnumerator();
                        _state = 2;
                        goto case 2;
                    case 2:
                        while (_enumerator.MoveNext())
                        {
                            TSource item = _enumerator.Current;
                            if (_predicate(item))
                            {
                                _current = _selector(item);
                                return true;
                            }
                        }

                        Dispose();
                        break;
                }

                return false;
            }

            public override IEnumerable<TResult2> Select<TResult2>(Func<TResult, TResult2> selector) =>
                new WhereSelectListIterator<TSource, TResult2>(_source, _predicate, CombineSelectors(_selector, selector));
        }
  1. 在构造方法中获取List对象,过滤条件和Select中的参数Selector委托参数。
  2. 覆盖Clone方法,对于WhereSelectListIterator,克隆方法返回WhereSelectListIterator实例。
  3. 按照List的结构,覆盖MoveNext()方法:
    1. 获取List的迭代器。
    2. Iterator类中已经定义了_state初始值是1,修改为2后,进入case 2。
    3. 使用List迭代器获取List内元素。
    4. 如果满足过滤条件,调用Selector,将参加迭代的元素转换为Select中指定的类型,返回true,否则返回false。
    5. 释放当前迭代器。
  4. 为应对Where().Select(selector1).Select(selector2)的调用方式,复写Select方法,返回WhereSelectListIterator实例, 构造方法的第三个参数是selector1和selector2合并后的新的转换器。

就是说形如Where(filter1).Where(filter2)…Where(filtern).Select(selector1)…Select(Selectorn)的调用, 无论有多少的个Select或多少的Where,在实际执行的时候,最后只有一个迭代器WhereSelectListIterator实例参与运算。所有的Where中的条件都会按照And的方式合并,所有Selector中的转化器都会按照Selectn(Selctorn-1(…Sector1))的方式进行合并。

上述结论对于Array和Enumerable容器同样适用,WhereEnumerableIterator ,WhereSelectEnumerableIterator, WhereArrayIterator ,WhereSelectArrayIterator源码与上述对应的迭代器对应代码类似,不再赘述。

上述结论不适用于Where的过滤方法需要索引参与运算的情况。

带索引参数的Where 扩展方法

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate)
        {
            if (source == null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
            }

            if (predicate == null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.predicate);
            }

            return WhereIterator(source, predicate);
        }
   private static IEnumerable<TSource> WhereIterator<TSource>(IEnumerable<TSource> source, Func<TSource, int, bool> predicate)
        {
            int index = -1;
            foreach (TSource element in source)
            {
                checked
                {
                    index++;
                }

                if (predicate(element, index))
                {
                    yield return element;
                }
            }
        }
  1. 参数检查同Where扩展方法1。
  2. 代码中并没有对线程对资源的占用情况进行检查,而是直接调用了WhereIterator 方法。
  3. WhereIterator 方法中维护了一个计数器,每循环一次,计数器加1,计数器中如果出现整型数字溢出情况,则抛出异常。
  4. 如果在多线程调用情况,foreach循环和计数器的同步可能存在问题。
  5. 在Where(filter1).Where(filter2)…Where(filtern).Select(selector1)…Select(Selectorn)调用下,该方法无法规约为一个迭代器,所有的filter将被在各自的过滤器中逐个检查。

FAQ

假设我有个一个List非空对象branchList,如果调用branchList.Where(s => s.BranchStatus == BranchStatus. Open ). Select(s => new {s.Name, S.Address}), 查询分行信息,为什么不加toList() 或foreach不能获取具体数据?

当前只能返回一个WhereSelectListIterator实例,只有通过toList或foreach循环,启动迭代器内部的MoveNext方法中启动该迭代器,从而获取数据。

在调用Where方法之前是否有必要进行List非空检查和List内元素个数不为0的检查?

  1. 对于List对象非空检查,还是需要的。如果对象为空,Where方法将抛出异常。
  2. 对于元素长度非0的检查,完全没有必要,Where方法内部已经进行的检查。

假设我有个一个List非空对象branchList,如果调用branchList.Where(s => s.BranchStatus == BranchStatus. Open ).Where(s. City == “Beijing”).Select(s => new {s.Name, S.Address}), 查询正常运营并且坐落在北京的分行信息,并返回分行的名称和地址,具体执行流程是什么 ?

  1. 进入Where扩展方法,检查分行List对象是否为空,本例不为空。
  2. 检查一个个Where的过滤条件是否为空,第一个过滤条件是分行正常运营s.BranchStatus == BranchStatus. Open,不为空。
  3. 源码中souce的实参对应是本例的List,所以先实例化WhereListIterator对象,返回new WhereListIterator(branchList, s.BranchStatus == BranchStatus. Open)。
  4. 再次进入扩展方法Where,即本例中的第二个Where调用。此时的参数source对象对应的实参是WhereListIterator对象,if (source is Iterator iterator)判断满足,调用WhereListIterator对象已经复写好的Where,即 iterator.Where(s. City == “Beijing”);
  5. 两Where中的过滤条件被以And方式合并。 返回new WhereListIterator(branchList, CombinePredicates(s.BranchStatus == BranchStatus. Open, s. City == “Beijing”))
  6. 进入Select扩展方法(见附录),Select扩展方法依然包含
    if (source is Iterator iterator)
    {
    return iterator.Select(selector);
    }
    的判断。
  7. 此时的source 是WhereListIterator实例,所以调用的Select 方法来自WhereListIterator对象中的Select,返回一个WhereSelectListIterator实例new WhereSelectListIterator(branchList, s.BranchStatus == BranchStatus. Open && s. City == “Beijing”, s => new {s.Name, S.Address})
  8. 通过ToList()或foreach调用该迭代时时候,MoveNext方法中的predict是s.BranchStatus == BranchStatus. Open && s. City == “Beijing”,selector是s => new {s.Name, S.Address},也就是说三个方法中的内容被合并到了一个迭代器执行。

在Where扩展方法中,如果是一个和Where不相关的迭代器调用,例如DefaultIfEmptyIterator 也继承自 Iterator类,对于XX.DefaultIfEmpty().Where()的调用方式,由于类DefaultIfEmptyIterator并不包含Where方法, 所以一旦执行到Where扩展方法后, 下面Where扩展方法中的代码是否会报错?

if (source is Iterator iterator)
{
return iterator.Where(predicate);
}

程序不会报错。DefaultIfEmptyIterator迭代器或其它Linq中的迭代器都继承自Iterator,在Iterrator中已经定义了虚方法Where,派生类可以选择重写该方法,也可以不重写该方法。此处(iterator.Where(predicate))中的Where方法则是来自基类Iterator的Where方法,返回的是默认的WhereEnumerableIterator迭代器。

附录

  /// 
        /// Combines two predicates.
        /// 
        /// The type of the predicate argument.
        /// The first predicate to run.
        /// The second predicate to run.
        /// 
        /// A new predicate that will evaluate to true only if both the first and
        /// second predicates return true. If the first predicate returns false,
        /// the second predicate will not be run.
        /// 
        public static Func<TSource, bool> CombinePredicates<TSource>(Func<TSource, bool> predicate1, Func<TSource, bool> predicate2) =>
            x => predicate1(x) && predicate2(x);

        /// 
        /// Combines two selectors.
        /// 
        /// The type of the first selector's argument.
        /// The type of the second selector's argument.
        /// The type of the second selector's return value.
        /// The first selector to run.
        /// The second selector to run.
        /// 
        /// A new selector that represents the composition of the first selector with the second selector.
        /// 
        public static Func<TSource, TResult> CombineSelectors<TSource, TMiddle, TResult>(Func<TSource, TMiddle> selector1, Func<TMiddle, TResult> selector2) =>
            x => selector2(selector1(x));
public static IEnumerable<TResult> Select<TSource, TResult>(
            this IEnumerable<TSource> source, Func<TSource, TResult> selector)
        {
            if (source == null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
            }

            if (selector == null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.selector);
            }

            if (source is Iterator<TSource> iterator)
            {
                return iterator.Select(selector);
            }

            if (source is IList<TSource> ilist)
            {
                if (source is TSource[] array)
                {
                    return array.Length == 0 ?
                        Empty<TResult>() :
                        new SelectArrayIterator<TSource, TResult>(array, selector);
                }

                if (source is List<TSource> list)
                {
                    return new SelectListIterator<TSource, TResult>(list, selector);
                }

                return new SelectIListIterator<TSource, TResult>(ilist, selector);
            }

            if (source is IPartition<TSource> partition)
            {
                IEnumerable<TResult>? result = null;
                CreateSelectIPartitionIterator(selector, partition, ref result);
                if (result != null)
                {
                    return result;
                }
            }

            return new SelectEnumerableIterator<TSource, TResult>(source, selector);
        }

你可能感兴趣的:(.Net,Core,C#基础,.Net)