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

概要

本文主要对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;
      }
  }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

cpolar做一个内网穿透

因为不在公司&#xff0c;需要访问公司的数据库&#xff0c;所以做一个内网穿透 下载安装 下载地址&#xff1a; https://dashboard.cpolar.com/get-started 下载后是个压缩包&#xff0c;解压后傻瓜式安装 操作隧道 安装后打开Cpolar Web UI 登录账号&#xff0c;查看隧…

mfc140u.dll丢失如何修复?解析mfc140u.dll是什么文件跟修复方法分享

大家好&#xff01;今天&#xff0c;我将和大家分享一下关于计算机中mfc140u.dll丢失的6种解决方法。希望我的分享能对大家在计算机使用过程中遇到问题时提供一些帮助。 首先&#xff0c;我想请大家了解一下什么是mfc140u.dll文件。mfc140u.dll是一个动态链接库文件&#xff0…

kali的学习

网络配置 1.kali的网络设置 首先我们了解kali的网络设置 DHCP&#xff1a;动态主机配置协议 是一个局域网的协议 使用UDP 协议工作静态IP&#xff1a;用于大部分的中小型网络 通过网络管理员手动分配IP原理进程 /etc 系统大部分服务启动过程都要访问该目录 我们直接去看看…

(六)k8s实战-存储管理

一、Volumes 1、HostPath 【使用场景&#xff1a;容器目录 挂载到 主机目录】 【可以持久化到主机上】 将节点上的文件或目录挂载到 Pod 上&#xff0c;此时该目录会变成持久化存储目录&#xff0c;即使 Pod 被删除后重启&#xff0c;也可以重新加载到该目录&#xff0c;该目…

Matlab 基本教程

1 清空环境变量及命令 clear all % 清除Workspace 中的所有变量 clc % 清除Command Windows 中的所有命令 2 变量命令规则 &#xff08;1&#xff09;变量名长度不超过63位 &#xff08;2&#xff09;变量名以字母开头&#xff0c; 可以由字母、数字和下划线…

Web后端开发(请求响应)上

请求响应的概述 浏览器&#xff08;请求&#xff09;<--------------------------(HTTP协议)---------------------->&#xff08;响应&#xff09;Web服务器 请求&#xff1a;获取请求数据 响应&#xff1a;设置响应数据 BS架构&#xff1a;浏览器/服务器架构模式。…

《华为认证》二层EVPN的配置

步骤1&#xff1a;配置PE和P设备的IGP以及mpls、mpls ldp&#xff08;略&#xff09; 步骤2&#xff1a;配置evpn实例&#xff0c;并且绑定到BD中&#xff0c;配置evpn的源ip地址 PE1: evpn vpn-instance 1 bd-mode //指定创建BD模式EVPN实例 route-distinguisher 100:1 vpn-…

学乐多光屏 P90:打开儿童学习新视界

随着科技迅猛发展&#xff0c;儿童教育正在迎来一场前所未有的革命。在这个数字化时代的浪潮中&#xff0c;学乐多光屏P90凭借其卓越的特性和深远的教育理念&#xff0c;成为智能儿童学习领域的引领者&#xff0c;为孩子们创造了崭新的学习体验。 创新科技&#xff0c;引领学习…

Android片段

如果你希望应用根据不同的环境有不同的外观和行为&#xff0c;这种情况下就需要片段&#xff0c;片段是可以由不同活动重用的模块化代码组件。 片段&#xff08;Fragment&#xff09;是活动&#xff08;Activity&#xff09;的一种模块化部分&#xff0c;表示活动中的行为或界面…

python-数据可视化-使用API

使用Web应用程序编程接口 &#xff08;API&#xff09;自动请求网站的特定信息而不是整个网页&#xff0c;再对这些信息进行可视化 使用Web API Web API是网站的一部分&#xff0c;用于与使用具体URL请求特定信息的程序交互。这种请求称为API调用 。请求的数据将以易于处理的…

开源图形驱动在OpenHarmony上的使用和落地

本文转载自 OpenHarmony TSC 官方微信公众号《峰会回顾第10期 | 开源图形驱动在OpenHarmony上的使用和落地》 演讲嘉宾 | 黄 然 回顾整理 | 廖 涛 排版校对 | 李萍萍 嘉宾简介 黄然&#xff0c;华为终端BG软件部资深图形技术专家&#xff0c;华为终端游戏标准、工具和分析创…

SpringBoot—日志

目录 日志使用日志日志级别设置日志级别设置分组指定日志文件路径日志切割归档使用第三方日志框架log4j2配置文件【分级存储】logback配置文件【分级存储】 实例代码 日志 使用日志 给controller添加日志信息 要给controller类上添加Slf4j注解&#xff0c;然后使用log.info(…

高速公路自动驾驶汽车超车控制方法研究

目录 摘要 ............................................................................................................ I Abstract ...................................................................................................... II 目录 ...............…

Ansible-palybook学习

目录 一.playbook介绍二.playbook格式1.书写格式2.notify介绍 一.playbook介绍 playbook 是 ansible 用于配置&#xff0c;部署&#xff0c;和管理被控节点的剧本。通过 playbook 的详细描述&#xff0c;执行其中的一系列 tasks &#xff0c;可以让远端主机达到预期的状态。pl…

electron软件安装时,默认选择为全部用户安装

后续可能会用electron开发一些工具&#xff0c;包括不限于快速生成个人小程序、开发辅助学习的交互式软件、帮助运维同学一键部署的简易版CICD工具等等。 开发进度&#xff0c;取决于我懒惰的程度。 不过不嫌弃的同学还是可以先关注一波小程序&#xff0c;真的发布工具了&…

java八股文面试[多线程]——synchronized锁升级过程

速记&#xff1a;偏向-轻量-重量 锁膨胀 上面讲到锁有四种状态&#xff0c;并且会因实际情况进行膨胀升级&#xff0c;其膨胀方向是&#xff1a;无锁——>偏向锁——>轻量级锁——>重量级锁&#xff0c;并且膨胀方向不可逆 一.锁升级理论. 在synchronized锁升级过程…

Qt应用开发(基础篇)——进度条 QProgressBar

一、前言 QProgressBar类继承于QWidget&#xff0c;是一个提供了横向或者纵向进度条的小部件。 QProgressBar进度条一般用来显示用户某操作的进度&#xff0c;比如烧录、导入、导出、下发、上传、加载等这些需要耗时和分包的概念&#xff0c;让用户知道程序还在正常的执行中。 …

web功能测试方法大全—完整!全面!(纯干货,建议收藏哦~)

本文通过六个部分为大家梳理了web功能测试过程中&#xff0c;容易出现的遗漏的部分&#xff0c;用以发掘自己工作中的疏漏。&#xff08;纯干货&#xff0c;建议收藏哦~&#xff09; 一、输入框 1、字符型输入框 2、数值型输入框 3、日期型输入框 4、信息重复 在一些需要命名…

做一个蛋糕店小程序需要哪些步骤?

对于一些不懂技术的新手来说&#xff0c;创建蛋糕店小程序可能会感到有些困惑。但是&#xff0c;有了乔拓云平台的帮助&#xff0c;你可以轻松地创建自己的蛋糕店小程序。下面&#xff0c;我将为大家详细介绍一下具体的操作步骤。 首先&#xff0c;登录乔拓云平台并进入后台管理…

使用这个插件,fiddler抓包直接生成httprunner脚本

har2case可以将.har文件转化成yaml格式或者json格式的httprunner的脚本文件&#xff0c;生成.har格式文件可以借助 fiddler 或 Charles 抓包工具 友情提示&#xff1a; 录制脚本&#xff0c;只是一个过渡&#xff0c;从0到1的一个过渡&#xff0c;如果让你直接写脚本&#xf…