C#Linq技术中SelectMany(...)函数的内部实现的伪代码
我们先来假设这种场景:
一个学校中有多个年级,一个年级有多个班级,一个班级里有多个学生。这里我们只需要班级、年级、和学生这三个概念;
让我们先来定义Class类和Student类:
- // 注意,Class是班级而不是 教室的意思,教室是 Classroom。
- public class Class
- {
- public int ClassId { get; set; }
- // 同一个班级的学生必然是属于同一个年级的,故GradeId直接在Class中声明就可以了。
- public int GradeId { get; set; }
- public List<Student> Students { get; set; }
- }
- public class Student
- {
- public int StudentId { get; set; }
- // 外键 ClassId
- public int ClassId { get; set; }
public string OtherProp{ get; set; }- // 注意,学生没有GradeId属性
- }
现在来声明多个班级:
- List<Class> classes = new List<Class>();
- classes.AddRange(...); // 这里添加的Class是有属于不同年级的
现在来看SelectMany(...);函数的声明:
- 声明一:
public static IEnumerable<TResult> SelectMany<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector);- 声明二:
- public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector);
对于第一个声明:我们调用该函数的形式即为:
- classes.SelectMany(cls=>cls.Students); // 这里很重要的一点,Func<TSource,IEnumerable<TResult>>里面的不是参数,而是类型,Func中最后一个泛型一定是返回值的泛型类型
// 这里classes 就是函数原型声明中的 source,而TSource就是Class类型,TResult就是 Student类型;而对于委托参数 selector中的 IEnumerable<TResult>实际上就是当前cls
// 代表的班级的所有学生,直接用cls.Students即可(而不是说自己声明一个 IEnumerable<Student> studs,然后将studs传到Func中,该Func只有一个参数)。
如上所示的代码实际上就是将多个班级classes中的所有学生都选出来返回一个 IEnumerable<Student> students,但是这里会存在一个问题,就是students中的学生
是classes中的学生总和,但是这时候我们无法知道students中某学生所属的年级是哪个了,如果想在返回classes中所有学生的集合的同时,对Student删减一些属性和
增加GradeId属性,这时候可以用第二个声明的SelectMany(..)函数,调用方法如下:
- // 这里 cls就是声明二中的对应第一个委托Func的参数,TSource就是Class类型,cls.Students即是IEnumerable<TCollection>类型返回值,TCollection就是Student类型
// 而stud 就是cls.Students中的某一个元素,new{....}返回的类型是匿名类型,就是 TResult类型。
classes.SelectMany(cls=>cls.Students,(cls,stud)=>new{- ClassId=stud.ClassId,GradeId=cls.GradeId,StudentId=stud.StudentId}); // 这里的属性的增减 形式是随自己需要来改变的,不一定就是要这样
这时候返回的 IEnumerable<TResult> ches变量就是 对原来的Student进行了属性增减后的新类型(匿名类型'a)的集合,ches集合的元素个数和之前
classes.SelectMany(cls=>cls.Students)返回的元素个数是一样的。这时候要遍历ches 就不能用具体类型,只能用XXX(var itm in ches) 了。
现在来看看第二个声明的SelectMany(...)的内部实现的一种方式及其伪代码:
- // TResult对应 'a匿名类型;TSource对应Class类型;TCollection对应Student类型
- // source 对应classes
- public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source,
- Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector)
- {
- IEnumerable<TResult> listResult = new IEnumerable<TResult>();
- // source 对应 classes
- foreach(var cls in source)
- {
- // TCollection对应Student,blockStudents对应 当前班级 cls中的所有学生集合
- // 给collectionSelector传 Lambda表达式时给出了该委托承载的 函数的具体声明 cls=>cls.Students。
- // Lambda表达式产生的 匿名函数的 参数类型,返回值类型一定是已知的,这里是Class和IEnumerable<Student>
- IEnumerable<TCollection> blockStudents = collectionSelector(cls);
- /* 如果是第一个声明,那么这时候就会开始执行 listResult.AddRange(blockStudents);此时Student即是TResult*/
- foreach(var stud in blockStudents) // 注意,这是第二层 foreach
- {
- // resultSelection(cls,stud) 返回的是 TResult,即匿名类型 'a 的实体对象,并添加到 listResult中
- // resultSelection承载的Lambda表达式为:(cls,stud)=>new {ClassId.......GradeId...} ;new出的
- // 匿名对象即为 TResult 类型。
- listResult.Add(resultSelection(cls, stud));
- } // Second Foreach End
- } // First Foreach End
- return listResult;
- } // SelectMany(...) End
C#Linq技术中SelectMany(...)函数的内部实现的伪代码的更多相关文章
- C#Linq技术中SelectMany(...)的内部实现推测
对于声明为:public static IEnumerable<TResult> SelectMany<TSource, TResult>(this IEnumerable&l ...
- C++中的多态与虚函数的内部实现
1.什么是多态 多态性可以简单概括为“一个接口,多种行为”. 也就是说,向不同的对象发送同一个消息, 不同的对象在接收时会产生不同的行为(即方法).也就是说,每个对象可 ...
- Android逆向之旅---基于对so中的函数加密技术实现so加固
一.前言 今天我们继续来介绍so加固方式,在前面一篇文章中我们介绍了对so中指定的段(section)进行加密来实现对so加固 http://blog.csdn.net/jiangwei0910410 ...
- 改变JavaScript中函数的内部this指向!
改变JavaScript中函数的内部this指向! 第一种方法 call call 可以 调用函数 + 改变函数内的this指向! var obj = { name: 'lvhang' } funct ...
- [SAP ABAP开发技术总结]日期函数
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- 《APUE》中的函数整理
第1章 unix基础知识 1. char *strerror(int errnum) 该函数将errnum(就是errno值)映射为一个出错信息字符串,返回该字符串指针.声明在string.h文件中. ...
- 【PHP】最详细PHP从入门到精通(二)——PHP中的函数
PHP从入门到精通 之PHP中的函数 各位开发者朋友大家好,自上次更新PHP的相关知识,得到了大家的广泛支持.PHP的火爆程度不言而喻,函数作为PHP中极为重要的部分,应诸位的支持,博主继续跟进更新 ...
- 浅谈Kotlin中的函数
本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/UV23Uw_969oVhiOdo4ZKAw作者:连凌能 Kotlin,已经被Android官方 ...
- 《python解释器源码剖析》第12章--python虚拟机中的函数机制
12.0 序 函数是任何一门编程语言都具备的基本元素,它可以将多个动作组合起来,一个函数代表了一系列的动作.当然在调用函数时,会干什么来着.对,要在运行时栈中创建栈帧,用于函数的执行. 在python ...
随机推荐
- 第三方插件渗透攻击之KingView
类别:堆溢出 描述:本次渗透利用了KingView6.5.3 SCADA中的ActiveX插件中存在漏洞的方法调用target.ValidateUser(arg1, arg2),通过缓冲区溢出覆盖了S ...
- NOIP2017-普及组复赛第2题 题解
Description 图书馆中每本书都有一个图书编码,可以用于快速检索图书,这个图书编码是一个正整数. 每位借书的读者手中有一个需求码,这个需求码也是一个正整数.如果一本书的图书编码恰好以读者的需 ...
- Spring4 事务管理
Spring4 事务管理 本章是Spring4 教程中的最后一章,也是非常重要的一章.如果说学习IOC是知识的入门,那学习事务管理就是知识的提升.本章篇幅可能有一丢丢长,也有一丢丢难,需要读者细细品味 ...
- eclipse中Maven工程使用Tomcat7以上插件
Maven中使用tomcat:run命令默认是使用Tomcat6的版本, 现在要用到Tomcat7以上的版本,在eclipse的Maven工程中配置如下 第一步:在项目的pom里面加入如下配置: 官网 ...
- PHP时间戳和日期互转换
在php中我们要把时间戳转换日期可以直接使用date函数来实现,如果要把日期转换成时间戳可以使用strtotime()函数实现,下面我来给大家举例说明. 1.php中时间转换函数 strtotime ...
- C#设计模式之十八中介者模式(Mediator Pattern)【行为型】
一.引言 今天我们开始讲“行为型”设计模式的第五个模式,该模式是[中介者模式],英文名称是:Mediator Pattern.还是老套路,先从名字上来看看.“中介者模式”我第一次看到这个名称,我的理解 ...
- python并发编程之多线程一
一,什么是线程 线程也被称为轻量进程计算机科学术语,指运行中的程序的调度单位. 线程是进程中的实体,一个进程可以拥有多个线程,一个线程必须有一个父进程.线程不拥有系统资源,只有运行必须的一些数据结构: ...
- 实际应用中遇到TimedRotatingFileHandler不滚动的问题
需求: 程序每天晚上8点和10点定时运行,期望日志按日期记录 添加Handler部分代码如下: formatter = logging.Formatter("%(asctime)s %(fi ...
- Ubuntu 环境 TensorFlow (最新版1.4) 源码编译、安装
Ubuntu 环境 TensorFlow 源码编译安装 基于(Ubuntu 14.04LTS/Ubuntu 16.04LTS/) 一.编译环境 1) 安装 pip sudo apt-get insta ...
- OBS源码解析(1)main函数
int main(int argc, char *argv[]){#ifndef _WIN32 signal(SIGPIPE, SIG_IGN);#endif #ifdef _WIN32 /*Open ...