本篇记录了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<Student> Students; public Teacher(string order,List<Student> students)
{
this.Name = order; this.Students = students;
}
}

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

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

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

            List<Student> studentList = new List<Student>();
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<TResult> SelectMany<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, IEnumerable<TResult>> selector
)

这个用于IEnumerable<T>的扩展方法接受一个Func委托,根据你的需要再返回另一个IEnumerable<T>,配合Where真是秒杀二重foreach啊。
        有时候我们需要输出更复杂的结果集,比如校长想知道教出这3个考不及格的倒霉蛋的,到底是哪几个更加倒霉的老师。那我们就要用到SelectMany的另一个重载方法了:

public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(
this IEnumerable<TSource> source,
Func<TSource, IEnumerable<TCollection>> collectionSelector,
Func<TSource, TCollection, TResult> 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); });

[转]Linq使用心得——SelectMany替代二重foreach循环的更多相关文章

  1. 巧用array_map()和array_reduce()替代foreach循环

    1.array_reduce( $arr , callable $callback ) 使用回调函数迭代地将数组简化为单一的值. 其中$arr 为输入数组,$callback($result , $v ...

  2. CheckBox和控件Foreach循环

    .aspx代码例如以下: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Sam ...

  3. JavaScript数组forEach循环

    JavaScript数组forEach循环 今天写JavaScript代码把forEach循环数组忘记写法了,在此记录一下以防止未来忘记. let a = [1, 2, 3]; a.forEach(f ...

  4. C# foreach 循环遍历数组

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Cons ...

  5. JAVA中的for-each循环与迭代

    在学习java中的collection时注意到,collection层次的根接口Collection实现了Iterable<T>接口(位于java.lang包中),实现这个接口允许对象成为 ...

  6. foreach循环 Java

    第一次遇到foreach循环,是在PHP的数组中,同样,在Java数组中,也遇到了foreach循环,都是用来遍历数组(集合).遍历数组,首先想到的一般都是用while,do while,for循环, ...

  7. 集合框架遍历方式之——for-each循环

    从Java5起,在Java中有了for-each循环,可以用来循环遍历collection和array.Foreach循环允许你在无需保持传统for循环中的索引,或在使用iterator /ListI ...

  8. Java语法糖1:可变长度参数以及foreach循环原理

    语法糖 接下来几篇文章要开启一个Java语法糖系列,所以首先讲讲什么是语法糖.语法糖是一种几乎每种语言或多或少都提供过的一些方便程序员开发代码的语法,它只是编译器实现的一些小把戏罢了,编译期间以特定的 ...

  9. For-Each循环

    For-Each循环也叫增强型的for循环,或者叫foreach循环. For-Each循环是JDK5.0的新特性(其他新特性比如泛型.自动装箱等). For-Each循环的加入简化了集合的遍历. 语 ...

随机推荐

  1. AIX 压缩与归档

    .tar.Z 格式 压缩:  compress filename.tar 解压:  zcat filename.tar.Z           tar -xvf -   .tar.gz 格式 压缩:t ...

  2. ajax的几个面试题

    一.什么是AJAX(请谈一下你对Ajax的认识)AJAX是“Asynchronous JavaScript and XML”的缩写.他是指一种创建交互式网页应用的网页开发技术.Ajax包含下列技术:基 ...

  3. idea中实现类快速重写service方法 快捷键

    1.在实现类中 CTRL+O 快捷键,会弹出所有方法 2.选择service中的方法,会自动重写

  4. LRJ入门经典-0905邮票和信封305

    原题 LRJ入门经典-0905邮票和信封305 难度级别:B: 运行时间限制:1000ms: 运行空间限制:256000KB: 代码长度限制:2000000B 试题描述 假定一张信封最多贴5张邮票,如 ...

  5. 【转】flex中的labelFunction(combox和dataGrid)

    Flex中,对于显示一个字段,只需要指定对应字段属性给labelField即可,当需要上述所需要的功能的时候就得做个转换了,在Flex的基于List的组件都有一个labelFunction方法能很简单 ...

  6. Vijos——T 1082 丛林探险

    https://vijos.org/p/1082 描述 东非大裂谷中有一片神秘的丛林,是全世界探险家的乐园,著名黄皮肤探险家BB一直想去试试.正好我国科学家2005年4月将首次对东非大裂谷进行科考,B ...

  7. ArcGIS Engine中的重点类库介绍

    转自原文ArcGIS Engine中的重点类库介绍 System类库 System类库是ArcGIS体系结构中最底层的类库.System类库包含给构成ArcGIS的其他类库提供服务的组件.System ...

  8. vector要注意的点

    vector的元素类别T,必须具备assignable和copyable两个性质. vector的容量很重要: 1. 一旦内存重新配置,和vector元素相关的所有references.pointer ...

  9. GO语言为结构体排序

    package main import ( "fmt" "io/ioutil" "sort" "time" ) type ...

  10. 验证list的底层数据结构

    <STL源代码剖析>中,指出SGI STL的list底层数据结构式循环双向链表.而且在链表尾端留一个空白节点.让end指向它.因为是双向的,那么list的迭代器必须是Bidirection ...