博客
关于我
Linq使用心得——SelectMany替代二重foreach循环
阅读量:433 次
发布时间:2019-03-06

本文共 3732 字,大约阅读时间需要 12 分钟。

        本篇记录了Linq学习的心得,较为浅显,各位大牛请轻拍。

        学习Linq其实已经很久了,但是一直没有使用的习惯,故水平也始终没有提高。近来刻意强迫自己用Linq来替代C# 2.0的一些写法。这里有一些心得和各位分享一下。

        首先看下面两个类的定义:

class Student    {        public int Score { get; set; }        public Student(int score)        {            this.Score = score;        }    }    class Teacher    {        public string Name { get; set; }        public List
Students; public Teacher(string order,List
students) { this.Name = order; this.Students = students; } }

        用以上两个类构建集合如下:

List
teachers = new List
{ new Teacher("a",new List
{ new Student(100),new Student(90),new Student(30) }), new Teacher("b",new List
{ new Student(100),new Student(90),new Student(60) }), new Teacher("c",new List
{ new Student(100),new Student(90),new Student(40) }), new Teacher("d",new List
{ new Student(100),new Student(90),new Student(60) }), new Teacher("e",new List
{ new Student(100),new Student(90),new Student(50) }), new Teacher("f",new List
{ new Student(100),new Student(90),new Student(60) }), new Teacher("g",new List
{ new Student(100),new Student(90),new Student(60) }) };

        这里有7个老师,每个人有3个学生,总共21一个学生里又有3个倒霉蛋没考及格……我们想要获得这3个倒霉蛋的集合。C# 2.0的代码如下:

List
studentList = new List
(); foreach (var t in teachers) { foreach (var s in t.Students) { if (s.Score < 60) { studentList.Add(s); } } }

        已经写了N多这样的二重foreach,写的都要吐了,简直恨不得做成代码段。因为所有编程语言都能这么写,有人觉得C#简单,可好学了,抓到就写,民工专用,抄袭Java,微软出品,必属垃圾,明天正午12点之前就会被淘汰……

        反正我觉得C# 3.0之后是越来越难,越来越复杂。难道是年纪大了智商堪忧……那既然要反驳,今天我们就换个C# 3.0的新写法。首先是查询表达式的写法:

var list1 = from t in teachers                        from s in t.Students                        where s.Score < 60                        select s;

        是不是感觉好多了,就跟写SQL一样顺畅。而且一目了然。也许习惯于OOXX的.NET程序员不那么喜欢SQL的语法,那还可以试试Lamda表达式的写法:

  var list2 = teachers.SelectMany(t => t.Students).Where(s => s.Score < 60);

        嗯?怎么只有一行,这是不是太欺负人了,到时候公司数代码行数算工钱的时候怎么办……嗯……这种公司你还是离了吧……

        写到这里我不禁感慨起SelectMany的伟大了,太好用了。其实我们刚才只是用了最简单的SelectMany也就是这个方法:

public static IEnumerable
SelectMany
( this IEnumerable
source, Func
> selector)

        这个用于IEnumerable<T>的扩展方法接受一个Func委托,根据你的需要再返回另一个IEnumerable<T>,配合Where真是秒杀二重foreach啊。

        有时候我们需要输出更复杂的结果集,比如校长想知道教出这3个考不及格的倒霉蛋的,到底是哪几个更加倒霉的老师。那我们就要用到SelectMany的另一个重载方法了:

public static IEnumerable
SelectMany
( this IEnumerable
source, Func
> collectionSelector, Func
resultSelector)

        第一眼看上去有点晕,重点是第一个Func委托的返回值IEnumerable<TCollection>,会遍历作为第二个Func委托的参数TCollection传递,供你构建所需要的投影集合。这里给出一个例子,下面的代码选出了门下有不及格学生的倒霉蛋老师+门生的分数:

var list3 = teachers.SelectMany(                t => t.Students,                (t, s) => new { t.Name, s.Score })                .Where(n => n.Score < 60);

        在这里,校长大人得到的集合,不仅包含了所有不及格的分数,同时还对应了该分数学生的教师姓名。惨啊……

        那么评论中有同学提到了不光要通过Linq查询出来结果,还要进行批量更新。我当时看了也蒙了啊,因为Linq写得也不熟。后来仔细一想其实是可以曲线救国的。Linq是语言集成查询的意思,他的查询结果并不会影响原始的数据源。但是这并不能阻止你Select一个新的投影。

        由于不及格的倒霉蛋太多,校长决定给每个倒霉蛋再加10分:

var list4 = teachers.SelectMany(t => t.Students).Where(s => s.Score < 60).Select(n => new Student(n.Score + 10));

        那么这会不及格的就只剩2个人了。还有的同学说我就是不想写foreach,就是要写成lamada表达式,其实还是有整容成lamda的ForEach的,这里为了庆祝一下评论数超过2位数,给所有学生永久加10分,这回是真改原始数据了,不过还是用到了ForEach,哈哈:

  teachers.SelectMany(t => t.Students).ToList().ForEach(s => { s.Score = s.Score + 10; Console.WriteLine(s.Score); });

        最后谢谢各位的讨论。

       

 

 

转载地址:http://aymyz.baihongyu.com/

你可能感兴趣的文章
单向链表
查看>>
使用双端队列模拟栈
查看>>
分而治之——归并
查看>>
堆与堆排序
查看>>
关于计数排序
查看>>
JavaSE基础学习笔记----持续更新
查看>>
使用 C# 9 的records作为强类型ID - 初次使用
查看>>
CentOS Too Many Open Files 解决
查看>>