当前位置: 首页 > news >正文

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);returnsource 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;}}

相关文章:

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

概要 本文主要对Take的优化方法进行源码分析&#xff0c;分析Take在配合Select&#xff0c;Where等常用的Linq扩展方法使用时候&#xff0c;如何实现优化处理。 本文涉及到Select, Where和Take和三个方法的源码分析&#xff0c;其中Select, Where, Take更详尽的源码分析&…...

Python 和 C++ 使用细节差别

1. 循环中的可迭代对象长度 1. 循环中的可迭代对象长度 C 中&#xff0c;for循环中写明a.size()&#xff0c;每次循环这个值是重新计算的&#xff1b; # include “iostream” # include <vector> using namespace std;int main() {vector<int> a(10);int cnt 0…...

在Ubuntu Linux系统上安装RabbitMQ服务并解决公网远程访问问题

文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 RabbitMQ是一个在 AMQP(高级消息队列协议)基…...

葫芦娃自动预约-公众号代挂

效果 #小程序://航旅黔购/1nkYlNRVzm0Gg9x #小程序://贵旅优品/7zz6mtnSVgDfyqa #小程序://新联惠购/ibFdsuhWqIbczEd #小程序://贵盐黔品/u2TgExCUdkavrFe #小程序://空港乐购/ANkOOdqEeo71kah #小程序://遵航出山/ZkR7DQy1raoPxKD #小程序://乐旅商城/Ip5cgpJ7TLmRrWF #小程序…...

ESP32应用教程(0)— PMW3901MB光流传感器

文章目录 前言 1 传感器介绍 1.1 关键特征 1.2 关键参数 2 硬件概述 2.1 信号引脚 2.2 参考电路图 3 寄存器 3.1 寄存器列表 3.2 性能优化寄存器 4 代码说明 4.1 结构体说明 4.2 编译说明 5 波形分析 前言 本文介绍了在 ESP32 DEVKIT V1 开发板上开发 PMW3901MB…...

docker部署nginx,部署springboot项目,并实现访问

一、先部署springboot项目 1、安装docker&#xff1a; yum install docker -y 2、启动docker&#xff1a; service docker start 重启&#xff1a; service docker restart 3、查看版本&#xff1a; docker -v 4、使设置docker.service生效&#xff08;路径&#xff1a;…...

十五、模板方法模式

一、什么是模板方法模式 模板方法&#xff08;Template Method&#xff09;模式的定义如下&#xff1a;定义一个操作中的算法骨架&#xff0c;而将算法的一些步骤延迟到子类中&#xff0c;使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。 模板方法模式包含以…...

jvm 什么是常量池,常量池定义 class常量池

首先需要理解下Java的class文件&#xff0c;以及class文件结构&#xff1a; 1.Class文件是一组以8个字节为基础单位的二进制流&#xff0c;各个数据项目严格按照顺序紧凑地排列在文 件之中&#xff0c;中间没有任何分隔符&#xff0c;这使得整个Class文件中存储的内容几乎全部…...

CA证书颁发机构服务器

目录 一、CA证书颁发机构是什么&#xff1f; 二、数字证书可以干什么&#xff1f; 三、PKI&#xff1a;即公钥加密体系&#xff08;public key cryptography&#xff09; 四、CA在网络中的工作流程及原理&#xff08;以网站为例&#xff09; 五、HTTPS 的工作原理 六、CA私有证…...

5. 线性层及其他层

5.1 神经网络结构 5.2 线性拉平 import torch import torchvision from torch import nn from torch.nn import ReLU from torch.nn import Sigmoid from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriterdataset torchvision.datase…...

PhpStorm安装篇

PhpStorm安装篇: 下载地址 : 进入官网下PhpStorm: PHP IDE and Code Editor from JetBrains 下载完之后&#xff0c;安装包 安装目录建议不要放C盘&#xff0c;放其他盘&#xff0c;其他直接一直点 next &#xff0c;到结束 安装完&#xff0c;打开编辑器 注册账号并登录后…...

麒麟Linux常见问题

文章目录 麒麟Linux常见问题 1. yum下载不了文件1&#xff09;报错信息2&#xff09;原因3&#xff09;解决&#xff08;1&#xff09;更换下载源&#xff08;2&#xff09;缓存处理 4&#xff09;验证 2. 麒麟Linux常见问题 1. yum下载不了文件 1&#xff09;报错信息 faile…...

一百六十八、Kettle——用海豚调度器定时调度从Kafka到HDFS的任务脚本(持续更新追踪、持续完善)

一、目的 在实际项目中&#xff0c;从Kafka到HDFS的数据是每天自动生成一个文件&#xff0c;按日期区分。而且Kafka在不断生产数据&#xff0c;因此看看kettle是不是需要时刻运行&#xff1f;能不能按照每日自动生成数据文件&#xff1f; 为了测试实际项目中的海豚定时调度从…...

Linux centos7 bash编程(小练习)

一、打印九九乘法口诀 这一个for循环嵌套的小练习&#xff0c;难度不大。提供一种写法&#xff0c;供参考&#xff1a; #!/bin/bash # 文件名&#xff1a;99table.sh # 打印输出九九乘法口诀表 for i in {1..9} do for ((j1;j<$i;j)) do …...

【SpringBoot】Web server failed to start. Port 8080 was already in use.

问题描述 SpringBoot启动Web服务器失败。 *************************** APPLICATION FAILED TO START ***************************Description:Web server failed to start. Port 8080 was already in use.Action:Identify and stop the process thats listening on port 80…...

day-36 代码随想录算法训练营(19)part05

435.无重叠区间 思路&#xff1a;首先对数组排序&#xff0c;只需要关注重叠区间就行&#xff0c;有重叠时计数1&#xff0c;然后更新当前右边界为重叠区间中的最小右边界。 763.划分字母区间 思路&#xff1a;记录每一个字母的最远位置&#xff0c;然后从头开始遍历&#xf…...

Vue3 实现JS动态改变CSS样式

以颜色为例子 定义颜色变量 import { reactive } from vue; // 可变的主题颜色 let chooseColor reactive({--color: #be2a27 }) CSS中使用 var() 函数引用颜色变量(这里是用elementPlus为例, 也可以改) :deep(.is-active) {color: var(--color);border-bottom: 2px solid…...

最新社区团购电商小程序源码 无bug完美运营版+详细搭建部署教程

分享一个开源社区团购电商小程序源码&#xff0c;无bug完美运营版&#xff0c;含完整前后端详细搭建部署教程。 系统运营模式&#xff1a;整合线下社区资源&#xff0c;由各快递代收点、社区便利店、社区物业、业主等发起的社区微信群&#xff0c;推送商品信息&#xff0c;消费…...

恒运资本:三大指数震荡走低,地产股大幅回撤,光刻胶概念逆市上涨

周四&#xff08;8月31日&#xff09;&#xff0c;到上午收盘&#xff0c;A股三大指数震动走低。其间&#xff0c;上证指数跌0.53%&#xff0c;报3120.39点&#xff1b;深证成指和创业板指别离跌0.55%、0.54%&#xff1b;沪深两市算计成交额5290.51亿元&#xff0c;总体来看&am…...

DP读书:不知道干什么就和我一起读书吧——以《鲲鹏处理器 架构与编程》中鲲鹏软件的构成为例

DP读书&#xff1a;不知道干什么就和我一起读书吧 为啥写博客&#xff1a;好处一&#xff1a;记录自己的学习过程优点二&#xff1a;让自己在各大社群里不那么尴尬推荐三&#xff1a;坚持下去&#xff0c;找到一个能支持自己的伙伴模版&#xff1a;鲲鹏软件构成硬件特定软件1. …...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...