zoukankan      html  css  js  c++  java
  • C#Linq技术中SelectMany(...)函数的内部实现的伪代码

    我们先来假设这种场景:

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

    让我们先来定义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 }

    现在来声明多个班级:

    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
  • 相关阅读:
    几种常见的树:排序二叉树、平衡二叉树、红黑树、B+树
    网关高可用
    微服务网关GateWay
    微服务网关Zuul
    客户端容错保护Alibaba Sentinel
    客户端容错保护Hystrix
    服务调用Feign
    服务注册与发现Consul
    服务负载均衡调用Ribbon
    服务注册Eureka高级
  • 原文地址:https://www.cnblogs.com/silentdoer/p/4772925.html
Copyright © 2011-2022 走看看