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
posted on 2015-08-31 14:01 Silentdoer 阅读(766) 评论(0) 编辑 收藏 举报