我们先来假设这种场景:

一个学校中有多个年级,一个年级有多个班级,一个班级里有多个学生。这里我们只需要班级、年级、和学生这三个概念;

让我们先来定义Class类和Student类:

  1.    // 注意,Class是班级而不是 教室的意思,教室是 Classroom。
  2. public class Class
  3. {
  4. public int ClassId { get; set; }
  5. // 同一个班级的学生必然是属于同一个年级的,故GradeId直接在Class中声明就可以了。
  6. public int GradeId { get; set; }
  7. public List<Student> Students { get; set; }
  8. }
  9.  
  10. public class Student
  11. {
  12. public int StudentId { get; set; }
  13. // 外键 ClassId
  14. public int ClassId { get; set; }
           public string OtherProp{ get; set; }
  15. // 注意,学生没有GradeId属性
  16. }

现在来声明多个班级:

  1. List<Class> classes = new List<Class>();
  2. classes.AddRange(...); // 这里添加的Class是有属于不同年级

现在来看SelectMany(...);函数的声明:

  1. 声明一:
    public static IEnumerable<TResult> SelectMany<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector);
  2. 声明二:
  3. public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector);

对于第一个声明:我们调用该函数的形式即为:

  1. 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(..)函数,调用方法如下:

  1. // 这里 cls就是声明二中的对应第一个委托Func的参数,TSource就是Class类型,cls.Students即是IEnumerable<TCollection>类型返回值,TCollection就是Student类型
    // 而stud 就是cls.Students中的某一个元素,new{....}返回的类型是匿名类型,就是 TResult类型。
    classes.SelectMany(cls=>cls.Students,(cls,stud)=>new{
  2. 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(...)的内部实现的一种方式及其伪代码:

  1.      // TResult对应 'a匿名类型;TSource对应Class类型;TCollection对应Student类型
  2. // source 对应classes
  3. public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source,
  4. Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector)
  5. {
  6. IEnumerable<TResult> listResult = new IEnumerable<TResult>();
  7. // source 对应 classes
  8. foreach(var cls in source)
  9. {
  10. // TCollection对应Student,blockStudents对应 当前班级 cls中的所有学生集合
  11. // 给collectionSelector传 Lambda表达式时给出了该委托承载的 函数的具体声明 cls=>cls.Students。
  12. // Lambda表达式产生的 匿名函数的 参数类型,返回值类型一定是已知的,这里是Class和IEnumerable<Student>
  13. IEnumerable<TCollection> blockStudents = collectionSelector(cls);
  14.  
  15. /* 如果是第一个声明,那么这时候就会开始执行 listResult.AddRange(blockStudents);此时Student即是TResult*/
  16.  
  17. foreach(var stud in blockStudents) // 注意,这是第二层 foreach
  18. {
  19. // resultSelection(cls,stud) 返回的是 TResult,即匿名类型 'a 的实体对象,并添加到 listResult中
  20. // resultSelection承载的Lambda表达式为:(cls,stud)=>new {ClassId.......GradeId...} ;new出的
  21. // 匿名对象即为 TResult 类型。
  22. listResult.Add(resultSelection(cls, stud));
  23. } // Second Foreach End
  24. } // First Foreach End
  25. return listResult;
  26. } // SelectMany(...) End

C#Linq技术中SelectMany(...)函数的内部实现的伪代码的更多相关文章

  1. C#Linq技术中SelectMany(...)的内部实现推测

    对于声明为:public static IEnumerable<TResult> SelectMany<TSource, TResult>(this IEnumerable&l ...

  2. C++中的多态与虚函数的内部实现

    1.什么是多态         多态性可以简单概括为“一个接口,多种行为”.         也就是说,向不同的对象发送同一个消息, 不同的对象在接收时会产生不同的行为(即方法).也就是说,每个对象可 ...

  3. Android逆向之旅---基于对so中的函数加密技术实现so加固

    一.前言 今天我们继续来介绍so加固方式,在前面一篇文章中我们介绍了对so中指定的段(section)进行加密来实现对so加固 http://blog.csdn.net/jiangwei0910410 ...

  4. 改变JavaScript中函数的内部this指向!

    改变JavaScript中函数的内部this指向! 第一种方法 call call 可以 调用函数 + 改变函数内的this指向! var obj = { name: 'lvhang' } funct ...

  5. [SAP ABAP开发技术总结]日期函数

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  6. 《APUE》中的函数整理

    第1章 unix基础知识 1. char *strerror(int errnum) 该函数将errnum(就是errno值)映射为一个出错信息字符串,返回该字符串指针.声明在string.h文件中. ...

  7. 【PHP】最详细PHP从入门到精通(二)——PHP中的函数

     PHP从入门到精通 之PHP中的函数 各位开发者朋友大家好,自上次更新PHP的相关知识,得到了大家的广泛支持.PHP的火爆程度不言而喻,函数作为PHP中极为重要的部分,应诸位的支持,博主继续跟进更新 ...

  8. 浅谈Kotlin中的函数

    本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/UV23Uw_969oVhiOdo4ZKAw作者:连凌能 Kotlin,已经被Android官方 ...

  9. 《python解释器源码剖析》第12章--python虚拟机中的函数机制

    12.0 序 函数是任何一门编程语言都具备的基本元素,它可以将多个动作组合起来,一个函数代表了一系列的动作.当然在调用函数时,会干什么来着.对,要在运行时栈中创建栈帧,用于函数的执行. 在python ...

随机推荐

  1. 第三方插件渗透攻击之KingView

    类别:堆溢出 描述:本次渗透利用了KingView6.5.3 SCADA中的ActiveX插件中存在漏洞的方法调用target.ValidateUser(arg1, arg2),通过缓冲区溢出覆盖了S ...

  2. NOIP2017-普及组复赛第2题 题解

    Description 图书馆中每本书都有一个图书编码,可以用于快速检索图书,这个图书编码是一个正整数.  每位借书的读者手中有一个需求码,这个需求码也是一个正整数.如果一本书的图书编码恰好以读者的需 ...

  3. Spring4 事务管理

    Spring4 事务管理 本章是Spring4 教程中的最后一章,也是非常重要的一章.如果说学习IOC是知识的入门,那学习事务管理就是知识的提升.本章篇幅可能有一丢丢长,也有一丢丢难,需要读者细细品味 ...

  4. eclipse中Maven工程使用Tomcat7以上插件

    Maven中使用tomcat:run命令默认是使用Tomcat6的版本, 现在要用到Tomcat7以上的版本,在eclipse的Maven工程中配置如下 第一步:在项目的pom里面加入如下配置: 官网 ...

  5. PHP时间戳和日期互转换

    在php中我们要把时间戳转换日期可以直接使用date函数来实现,如果要把日期转换成时间戳可以使用strtotime()函数实现,下面我来给大家举例说明. 1.php中时间转换函数 strtotime ...

  6. C#设计模式之十八中介者模式(Mediator Pattern)【行为型】

    一.引言 今天我们开始讲“行为型”设计模式的第五个模式,该模式是[中介者模式],英文名称是:Mediator Pattern.还是老套路,先从名字上来看看.“中介者模式”我第一次看到这个名称,我的理解 ...

  7. python并发编程之多线程一

    一,什么是线程 线程也被称为轻量进程计算机科学术语,指运行中的程序的调度单位. 线程是进程中的实体,一个进程可以拥有多个线程,一个线程必须有一个父进程.线程不拥有系统资源,只有运行必须的一些数据结构: ...

  8. 实际应用中遇到TimedRotatingFileHandler不滚动的问题

    需求: 程序每天晚上8点和10点定时运行,期望日志按日期记录 添加Handler部分代码如下: formatter = logging.Formatter("%(asctime)s %(fi ...

  9. Ubuntu 环境 TensorFlow (最新版1.4) 源码编译、安装

    Ubuntu 环境 TensorFlow 源码编译安装 基于(Ubuntu 14.04LTS/Ubuntu 16.04LTS/) 一.编译环境 1) 安装 pip sudo apt-get insta ...

  10. OBS源码解析(1)main函数

    int main(int argc, char *argv[]){#ifndef _WIN32 signal(SIGPIPE, SIG_IGN);#endif #ifdef _WIN32 /*Open ...