C# Linq源码分析之Take(四)

news2024/12/28 20:26:09

概要

本文主要对Take的优化方法进行源码分析,分析Take在配合Select,Where等常用的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 IEnumerable<TSource> TakeIterator<TSource>(IEnumerable<TSource> source, int count)
 {
     Debug.Assert(source != null);
     Debug.Assert(count > 0);

     return
         source is IPartition<TSource> partition ? partition.Take(count) :
         source is IList<TSource> sourceList ? new ListPartition<TSource>(sourceList, 0, count - 1) :
         new EnumerablePartition<TSource>(source, 0, count - 1);
 }

该优化方法的基本逻辑如下:

  1. 如果source序列实现了IPartition接口,则调用IPartition中的Take方法;
  2. 如果source序列实现了IList接口,则返回ListPartition对象;
  3. 返回EnumerablePartition对象。

案例分析

Select.Take

该场景模拟我们显示中将EF中与数据库关联的对象,转换成Web前端需要的对象,并分页的情况。

将Student对象中的学生姓名和考试成绩取出后,并分页,Student类的定义和初始化请见附录。

 studentList.Select(x => new {
                x.Name, x.MathResult
            }).take(3).ToList();

在这里插入图片描述

执行流程如上图所示:

  1. 进入Select方法后返回一个SelectListIterator对象,该对象存储了source序列数据和selector;
  2. 进入Take方法后, 因为SelectListIterator实现了IPartition接口,因此可以调用自己的Take方法,使用source,selector和count(Take方法的参数)实例化SelectListPartitionIterator对象;
  public IPartition<TResult> Take(int count)
{
    Debug.Assert(count > 0);
    int maxIndex = _minIndexInclusive + count - 1;
    return (uint)maxIndex >= (uint)_maxIndexInclusive ? this : new SelectListPartitionIterator<TSource, TResult>(_source, _selector, _minIndexInclusive, maxIndex);
}

  1. 进入ToList方法后,根据下面的代码,因为SelectListPartitionIterator对象实现了IPartition接口,而IPartition又继承了IIListProvider接口,所以listProvider.ToList()被执行。
   public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
        {
if (source == null)
 {
     ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
 }

 return source is IIListProvider<TSource> listProvider ? listProvider.ToList() : new List<TSource>(source);
}

listProvider.ToList() 被执行,即SelectListPartitionIterator对象内定义的ToList()方法被执行,该ToList方法可以将
Selecxt的投影操作,Take的取值操作同时执行,代码如下:

 public List<TResult> ToList()
{
    int count = Count;
    if (count == 0)
    {
        return new List<TResult>();
    }

    List<TResult> list = new List<TResult>(count);
    int end = _minIndexInclusive + count;
    for (int i = _minIndexInclusive; i != end; ++i)
    {
        list.Add(_selector(_source[i]));
    }

    return list;
}

  1. 如果Take的取值为0,直接返回空List,不会进行Select操作;
  2. 按照Take中指定的Count,取到相应的元素,再进行投影操作;
  3. 返回操作结果。

这样,源List序列source,Linq只遍历和一次,就同时完成了投影和过滤两个操作,实现了优化。

附录

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 IIListProvider<TElement> : IEnumerable<TElement>
{
    TElement[] ToArray();
    List<TElement> ToList();
    int GetCount(bool onlyIfCheap);
}

IPartition接口

internal interface IPartition<TElement> : IIListProvider<TElement>
{

    IPartition<TElement> Skip(int count);

    IPartition<TElement> Take(int count);

    TElement? TryGetElementAt(int index, out bool found);

    TElement? TryGetFirst(out bool found);

    TElement? TryGetLast(out bool found);
}

SelectListPartitionIterator类

 private sealed class SelectListPartitionIterator<TSource, TResult> : Iterator<TResult>, IPartition<TResult>
{
    private readonly IList<TSource> _source;
    private readonly Func<TSource, TResult> _selector;
    private readonly int _minIndexInclusive;
    private readonly int _maxIndexInclusive;

    public SelectListPartitionIterator(IList<TSource> source, Func<TSource, 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 Iterator<TResult> Clone() =>
        new SelectListPartitionIterator<TSource, 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 IEnumerable<TResult2> Select<TResult2>(Func<TResult, TResult2> selector) =>
        new SelectListPartitionIterator<TSource, TResult2>(_source, CombineSelectors(_selector, selector), _minIndexInclusive, _maxIndexInclusive);

    public IPartition<TResult> Skip(int count)
    {
        Debug.Assert(count > 0);
        int minIndex = _minIndexInclusive + count;
        return (uint)minIndex > (uint)_maxIndexInclusive ? EmptyPartition<TResult>.Instance : new SelectListPartitionIterator<TSource, TResult>(_source, _selector, minIndex, _maxIndexInclusive);
    }

    public IPartition<TResult> Take(int count)
    {
        Debug.Assert(count > 0);
        int maxIndex = _minIndexInclusive + count - 1;
        return (uint)maxIndex >= (uint)_maxIndexInclusive ? this : new SelectListPartitionIterator<TSource, 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.Empty<TResult>();
        }

        TResult[] array = new TResult[count];
        for (int i = 0, curIdx = _minIndexInclusive; i != array.Length; ++i, ++curIdx)
        {
            array[i] = _selector(_source[curIdx]);
        }

        return array;
    }

    public List<TResult> ToList()
    {
        int count = Count;
        if (count == 0)
        {
            return new List<TResult>();
        }

        List<TResult> list = new List<TResult>(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 SelectListIterator<TSource, TResult> : Iterator<TResult>
 {
     private readonly List<TSource> _source;
     private readonly Func<TSource, TResult> _selector;
     private List<TSource>.Enumerator _enumerator;

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

     private int CountForDebugger => _source.Count;

     public override Iterator<TResult> Clone() => new SelectListIterator<TSource, 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 IEnumerable<TResult2> Select<TResult2>(Func<TResult, TResult2> selector) =>
         new SelectListIterator<TSource, TResult2>(_source, CombineSelectors(_selector, selector));
 }

Select.SpeedOpt.cs

private sealed partial class SelectListIterator<TSource, TResult> : IPartition<TResult>
 {
      public TResult[] ToArray()
      {
          int count = _source.Count;
          if (count == 0)
          {
              return Array.Empty<TResult>();
          }

          var results = new TResult[count];
          for (int i = 0; i < results.Length; i++)
          {
              results[i] = _selector(_source[i]);
          }

          return results;
      }

      public List<TResult> ToList()
      {
          int count = _source.Count;
          var results = new List<TResult>(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 IPartition<TResult> Skip(int count)
      {
          Debug.Assert(count > 0);
          return new SelectListPartitionIterator<TSource, TResult>(_source, _selector, count, int.MaxValue);
      }

      public IPartition<TResult> Take(int count)
      {
          Debug.Assert(count > 0);
          return new SelectListPartitionIterator<TSource, 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;
      }
  }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/939628.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构--树4.2.2(二叉树--遍历)

目录 一、二叉树的建立 二、二叉树的遍历算法 一、二叉树的建立 CreateBitree(Bitree *t){char c;scanf("%c",&c);if( c){*t NULL;}else{*t(Bitnode*)malloc(sizeof(Bitnode));(*t)->data c;CreateBitree(&(*t)->lchild);CreateBitree(&(*t)-&…

机器学习——KNN回归

1、前提知识&#xff1a; 回归&#xff1a;可以理解为拟合&#xff0c;就是根据训练数据的趋势&#xff0c;对输入数据进行预测。KNN回归&#xff1a;是一种有监督学习&#xff0c;因为需要提供目标数据&#xff08;target&#xff09; 2、案例&#xff1a; 用KNN回归拟合sin…

爬虫异常处理之如何处理连接丢失和数据存储异常

在爬虫开发过程中&#xff0c;我们可能会遇到各种异常情况&#xff0c;如连接丢失、数据存储异常等。本文将介绍如何处理这些异常&#xff0c;并提供具体的解决代码。我们将以Python语言为例&#xff0c;使用requests库进行网络请求和sqlite3库进行数据存储。 1. 处理连接丢失 …

高忆管理:新手炒股入门零基础学?

炒股是一些人为了取得高额回报和更好的财政自由而进行的活动。但对许多新手而言&#xff0c;这是一个全新的领域&#xff0c;需求掌握许多根底常识才能够开始加入炒股商场。本文将为零根底的新手炒股入门供给一些主张和技巧&#xff1a; 一、学习根底常识 关于炒股入门的新手而…

Android 绘制之文字测量

drawText() 绘制文字 绘制进度条:paint.strokeCap Paint.CAP.RONUD 线条两边样式 设置文字字体:paint.typeFace Resources.Compat.getFont(context,font) 设置加粗 paint.isFakeBoldText 设置居中: paint.setTextAlign Paint.Align.CENTER //居中, 并不是真正的居中 往…

项目经理——任劳任怨的“背锅侠”

很多人可能觉得项目经理在工作中只需要动动嘴皮子&#xff0c;然后跟其他关系人搞好关系就行了&#xff0c;但是其实他们负责整个项目的规划、执行和交付&#xff0c;是整个项目顺利进行的关键。然而&#xff0c;在项目中面临着各种各样的挑战和压力。那么&#xff0c;作为项目…

[ES]安装es、kibana、ik分词器

一、安装es和kibana 1、创建一个网络&#xff0c;网络内的框架(eskibana)互联 docker network create es-net 2、下载es和kibana docker pull elasticsearch:7.12.1 docker pull kibana:7.12.1 3、运行docker命令部署单点eskibana&#xff08;用来操作es&#xff09; doc…

软件开发管理全过程文件、验收材料支撑下载

一、前言 软件开发工作中&#xff0c;在#夏天生活图鉴#计划&#xff0c;需求分析&#xff0c;开发实施&#xff0c;测试&#xff0c;评审&#xff0c;交付验收&#xff0c;以及投标等其他过程中&#xff0c;会产生大量的文档&#xff0c;为了规范项目整个流程&#xff0c;亦或是…

Elasticsearch 集成--Flink 框架集成

一、Flink 框架介绍 Apache Spark 是一种基于内存的快速、通用、可扩展的大数据分析计算引擎。 Apache Spark 掀开了内存计算的先河&#xff0c;以内存作为赌注&#xff0c;赢得了内存计算的飞速发展。 但是在其火热的同时&#xff0c;开发人员发现&#xff0c;在 Spark …

青蛙趣味支付页html源码

青蛙因生活所迫卖儿卖HTML单页源码.zip - 蓝奏云 源码自适应窗口&#xff0c;电脑和手机&#xff0c; 适合作为网站下载页&#xff0c;用于增加支付率 &#xff08;终于写好了&#xff0c;不太想写&#xff09;

3.3 运算符和表达式

前言&#xff1a; 几乎每一个程序都需要进行运算&#xff0c;对数据进行加工处理&#xff0c;否则程序就没有意义了。要进行运算&#xff0c;就需规定可以使用的运算符。C语言的运算符范围很宽&#xff0c;把除了控制语句和输入输出以外几乎所有的基本操作都作为运算符处理&am…

【百度之星2023】初赛第一场 补题(部分)

目录 BD202301 公园BD202302 蛋糕划分解法1TODO 解法2 TODO BD202303 第五维度TODO BD202304 流水线搭积木BD202305 糖果促销 不幸因为码蹄集客户端的bug&#xff0c;导致没法正常参与比赛&#xff0c;只好事后补了 BD202301 公园 样例输入&#xff1a; 4 4 3 1 2 8 8 1 4 2 …

AD如何进行汉化

AD如何进行汉化 通过安装好AD后&#xff0c;默认都是英文界面模式&#xff0c;如果想汉化为中文模式&#xff0c;需要点击“DXP”->“参数选择”&#xff0c;打开界面如下&#xff1a; 然后将上图“本地化”下面的方框勾选上&#xff0c;点击“应用”&#xff0c;“确定”…

Java通过报表技术JXL和POI实现Excel导入导出操作

前言 报表[forms for reporting to the higher organizations]&#xff0c;就是向上级报告情况的表格。简单的说&#xff1a;报表就是用表格、图表等格式来动态显示数据&#xff0c;可以用公式表示为&#xff1a;“报表 多样的格式 动态的数据”。 注意&#xff1a;使用附件…

删除流氓360首页

不管你使用什么浏览器都很容易中招360给你自动设置的流氓首页&#xff0c;流氓厂石锤了。 你在浏览器设置新的首页一样无效&#xff0c;比如 完全没有卵用&#xff0c;以前这样是可以生效的&#xff0c;最近几天突然不行了&#xff0c;这简直流氓的不行&#xff0c;而且 细心…

【Java 中级】一文精通 Spring MVC - JSON 处理(九)

&#x1f449;博主介绍&#xff1a; 博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家&#xff0c;WEB架构师&#xff0c;阿里云专家博主&#xff0c;华为云云享专家&#xff0c;51CTO 专家博主 ⛪️ 个人社区&#x…

【rar转zip】WinRAR转换压缩包格式

不知道大家有没有遇到需要转换压缩包格式的问题&#xff0c;今天想和大家分享rar压缩包改成zip格式的方法。 方法一&#xff1a; 直接修改rar压缩包的后缀名变为zip&#xff0c;就可以修改压缩包文件格式了 方法二&#xff1a; 先将rar压缩包解压出来&#xff0c;然后再将解…

Stable Diffusion WebUI 整合包

现在网络上出现的各种整合包只是整合了运行 Stable Diffusion WebUI&#xff08;以下简称为 SD-WebUI&#xff09;必需的 Python 和 Git 环境&#xff0c;并且预置好模型&#xff0c;有些整合包还添加了一些常用的插件&#xff0c;其实际与手动进行本地部署并没有区别。 不过&a…

热红外成像技术:未来将有更多技术突破推动应用发展

一、国外发展现状 热红外成像技术在国外得到了广泛的研究和应用。国外的研究机构和企业注重热红外成像技术在军事、环境监测、医疗等领域的应用研究&#xff0c;其中美国、欧洲和日本等国家在热红外成像技术方面处于领先地位。 美国在热红外成像技术方面拥有多个研究机构和公司…

后端面试话术集锦第三篇:spring cloud 相关面试话术

🚗后端面试集锦目录 💖后端面试话术集锦第一篇:spring面试话术💖 💖后端面试话术集锦第二篇:spring boot面试话术💖 💖后端面试话术集锦第三篇:spring cloud面试话术💖 💖后端面试话术集锦第四篇:ElasticSearch面试话术💖 1. 什么是Springcloud Spring …