兼职 做网站,山网站建设,台州网站制作台州网站建设,建筑作品集网站代做概要
本文主要对Take的优化方法进行源码分析#xff0c;分析Take在配合Select#xff0c;Where等常用的Linq扩展方法使用时候#xff0c;如何实现优化处理。
本文涉及到Select, Where和Take和三个方法的源码分析#xff0c;其中Select, Where, Take更详尽的源码分析…概要
本文主要对Take的优化方法进行源码分析分析Take在配合SelectWhere等常用的Linq扩展方法使用时候如何实现优化处理。
本文涉及到Select, Where和Take和三个方法的源码分析其中Select, Where, Take更详尽的源码分析请参考我之前写的文章。
源码分析
我们之前对Take的源码分析主要是真对数据序列对象直接调用的Take方法的情况。本文介绍的Take优化方法主要是针对多个Linq方法配合使用的情况例如xx.Where.Select.Take或者xx.Select.Take的情况。
Take的优化方法是定义在Take.SpeedOpt.cs文件中体现在下面的TakeIterator方法中Take方法对TakeIterator方法的具体调用方式请参考C# Linq源码分析之Take方法
private static IEnumerableTSource TakeIteratorTSource(IEnumerableTSource source, int count){Debug.Assert(source ! null);Debug.Assert(count 0);returnsource is IPartitionTSource partition ? partition.Take(count) :source is IListTSource sourceList ? new ListPartitionTSource(sourceList, 0, count - 1) :new EnumerablePartitionTSource(source, 0, count - 1);}该优化方法的基本逻辑如下
如果source序列实现了IPartition接口则调用IPartition中的Take方法如果source序列实现了IList接口则返回ListPartition对象返回EnumerablePartition对象。
案例分析
Select.Take
该场景模拟我们显示中将EF中与数据库关联的对象转换成Web前端需要的对象并分页的情况。
将Student对象中的学生姓名和考试成绩取出后并分页Student类的定义和初始化请见附录。 studentList.Select(x new {x.Name, x.MathResult}).take(3).ToList();执行流程如上图所示
进入Select方法后返回一个SelectListIterator对象该对象存储了source序列数据和selector进入Take方法后 因为SelectListIterator实现了IPartition接口因此可以调用自己的Take方法使用sourceselector和countTake方法的参数实例化SelectListPartitionIterator对象 public IPartitionTResult Take(int count)
{Debug.Assert(count 0);int maxIndex _minIndexInclusive count - 1;return (uint)maxIndex (uint)_maxIndexInclusive ? this : new SelectListPartitionIteratorTSource, TResult(_source, _selector, _minIndexInclusive, maxIndex);
}
进入ToList方法后根据下面的代码因为SelectListPartitionIterator对象实现了IPartition接口而IPartition又继承了IIListProvider接口所以listProvider.ToList()被执行。 public static ListTSource ToListTSource(this IEnumerableTSource source){
if (source null){ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);}return source is IIListProviderTSource listProvider ? listProvider.ToList() : new ListTSource(source);
}listProvider.ToList() 被执行即SelectListPartitionIterator对象内定义的ToList()方法被执行该ToList方法可以将 Selecxt的投影操作Take的取值操作同时执行代码如下 public ListTResult ToList()
{int count Count;if (count 0){return new ListTResult();}ListTResult list new ListTResult(count);int end _minIndexInclusive count;for (int i _minIndexInclusive; i ! end; i){list.Add(_selector(_source[i]));}return list;
}
如果Take的取值为0直接返回空List不会进行Select操作按照Take中指定的Count取到相应的元素再进行投影操作返回操作结果。
这样源List序列sourceLinq只遍历和一次就同时完成了投影和过滤两个操作实现了优化。
附录
Student类
public class Student {public string Id { get; set; }public string Name { get; set; }public string Classroom { get; set; }public int MathResult { get; set; }
}IIListProvider接口
internal interface IIListProviderTElement : IEnumerableTElement
{TElement[] ToArray();ListTElement ToList();int GetCount(bool onlyIfCheap);
}IPartition接口
internal interface IPartitionTElement : IIListProviderTElement
{IPartitionTElement Skip(int count);IPartitionTElement Take(int count);TElement? TryGetElementAt(int index, out bool found);TElement? TryGetFirst(out bool found);TElement? TryGetLast(out bool found);
}SelectListPartitionIterator类 private sealed class SelectListPartitionIteratorTSource, TResult : IteratorTResult, IPartitionTResult
{private readonly IListTSource _source;private readonly FuncTSource, TResult _selector;private readonly int _minIndexInclusive;private readonly int _maxIndexInclusive;public SelectListPartitionIterator(IListTSource source, FuncTSource, TResult selector, int minIndexInclusive, int maxIndexInclusive){Debug.Assert(source ! null);Debug.Assert(selector ! null);Debug.Assert(minIndexInclusive 0);Debug.Assert(minIndexInclusive maxIndexInclusive);_source source;_selector selector;_minIndexInclusive minIndexInclusive;_maxIndexInclusive maxIndexInclusive;}public override IteratorTResult Clone() new SelectListPartitionIteratorTSource, TResult(_source, _selector, _minIndexInclusive, _maxIndexInclusive);public override bool MoveNext(){// _state - 1 represents the zero-based index into the list.// Having a separate field for the index would be more readable. However, we save it// into _state with a bias to minimize field size of the iterator.int index _state - 1;if (unchecked((uint)index (uint)(_maxIndexInclusive - _minIndexInclusive) index _source.Count - _minIndexInclusive)){_current _selector(_source[_minIndexInclusive index]);_state;return true;}Dispose();return false;}public override IEnumerableTResult2 SelectTResult2(FuncTResult, TResult2 selector) new SelectListPartitionIteratorTSource, TResult2(_source, CombineSelectors(_selector, selector), _minIndexInclusive, _maxIndexInclusive);public IPartitionTResult Skip(int count){Debug.Assert(count 0);int minIndex _minIndexInclusive count;return (uint)minIndex (uint)_maxIndexInclusive ? EmptyPartitionTResult.Instance : new SelectListPartitionIteratorTSource, TResult(_source, _selector, minIndex, _maxIndexInclusive);}public IPartitionTResult Take(int count){Debug.Assert(count 0);int maxIndex _minIndexInclusive count - 1;return (uint)maxIndex (uint)_maxIndexInclusive ? this : new SelectListPartitionIteratorTSource, TResult(_source, _selector, _minIndexInclusive, maxIndex);}public TResult? TryGetElementAt(int index, out bool found){if ((uint)index (uint)(_maxIndexInclusive - _minIndexInclusive) index _source.Count - _minIndexInclusive){found true;return _selector(_source[_minIndexInclusive index]);}found false;return default;}public TResult? TryGetFirst(out bool found){if (_source.Count _minIndexInclusive){found true;return _selector(_source[_minIndexInclusive]);}found false;return default;}public TResult? TryGetLast(out bool found){int lastIndex _source.Count - 1;if (lastIndex _minIndexInclusive){found true;return _selector(_source[Math.Min(lastIndex, _maxIndexInclusive)]);}found false;return default;}private int Count{get{int count _source.Count;if (count _minIndexInclusive){return 0;}return Math.Min(count - 1, _maxIndexInclusive) - _minIndexInclusive 1;}}public TResult[] ToArray(){int count Count;if (count 0){return Array.EmptyTResult();}TResult[] array new TResult[count];for (int i 0, curIdx _minIndexInclusive; i ! array.Length; i, curIdx){array[i] _selector(_source[curIdx]);}return array;}public ListTResult ToList(){int count Count;if (count 0){return new ListTResult();}ListTResult list new ListTResult(count);int end _minIndexInclusive count;for (int i _minIndexInclusive; i ! end; i){list.Add(_selector(_source[i]));}return list;}public int GetCount(bool onlyIfCheap){// In case someone uses Count() to force evaluation of// the selector, run it provided onlyIfCheap is false.int count Count;if (!onlyIfCheap){int end _minIndexInclusive count;for (int i _minIndexInclusive; i ! end; i){_selector(_source[i]);}}return count;}
}SelectListIterator类 Select.cs private sealed partial class SelectListIteratorTSource, TResult : IteratorTResult{private readonly ListTSource _source;private readonly FuncTSource, TResult _selector;private ListTSource.Enumerator _enumerator;public SelectListIterator(ListTSource source, FuncTSource, TResult selector){Debug.Assert(source ! null);Debug.Assert(selector ! null);_source source;_selector selector;}private int CountForDebugger _source.Count;public override IteratorTResult Clone() new SelectListIteratorTSource, TResult(_source, _selector);public override bool MoveNext(){switch (_state){case 1:_enumerator _source.GetEnumerator();_state 2;goto case 2;case 2:if (_enumerator.MoveNext()){_current _selector(_enumerator.Current);return true;}Dispose();break;}return false;}public override IEnumerableTResult2 SelectTResult2(FuncTResult, TResult2 selector) new SelectListIteratorTSource, TResult2(_source, CombineSelectors(_selector, selector));}Select.SpeedOpt.cs
private sealed partial class SelectListIteratorTSource, TResult : IPartitionTResult{public TResult[] ToArray(){int count _source.Count;if (count 0){return Array.EmptyTResult();}var results new TResult[count];for (int i 0; i results.Length; i){results[i] _selector(_source[i]);}return results;}public ListTResult ToList(){int count _source.Count;var results new ListTResult(count);for (int i 0; i count; i){results.Add(_selector(_source[i]));}return results;}public int GetCount(bool onlyIfCheap){// In case someone uses Count() to force evaluation of// the selector, run it provided onlyIfCheap is false.int count _source.Count;if (!onlyIfCheap){for (int i 0; i count; i){_selector(_source[i]);}}return count;}public IPartitionTResult Skip(int count){Debug.Assert(count 0);return new SelectListPartitionIteratorTSource, TResult(_source, _selector, count, int.MaxValue);}public IPartitionTResult Take(int count){Debug.Assert(count 0);return new SelectListPartitionIteratorTSource, TResult(_source, _selector, 0, count - 1);}public TResult? TryGetElementAt(int index, out bool found){if (unchecked((uint)index (uint)_source.Count)){found true;return _selector(_source[index]);}found false;return default;}public TResult? TryGetFirst(out bool found){if (_source.Count ! 0){found true;return _selector(_source[0]);}found false;return default;}public TResult? TryGetLast(out bool found){int len _source.Count;if (len ! 0){found true;return _selector(_source[len - 1]);}found false;return default;}}