[转]Linq使用心得——SelectMany替代二重foreach循环

本篇记录了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); });
时间: 2024-08-01 20:40:32

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

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

1.array_reduce( $arr , callable $callback ) 使用回调函数迭代地将数组简化为单一的值. 其中$arr 为输入数组,$callback($result , $value)接受两个参数,$result为上一次迭代产生的值,$value是当前迭代的值. 使用array_reduce()替代foreach()循环最常用的一个业务场景也许就是数组求和,比如: 1 $arr = array('1','2','3'); //计算数组中数字的和 2 $sum = 0;

CheckBox和控件Foreach循环

.aspx代码例如以下: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Sample 3-06.aspx.cs" Inherits="Sample_3_6" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w

多线程下的for循环和foreach循环 System.InvalidOperationException: 集合已修改;可能无法执行枚举

背景:当循环体在循环的时候有需求要修改自己.或者在多线程下,循环静态变量的时候,别人很容易修改了循环体内的数据.但是这就会报错的 准备:for:foeach:多线程. 解决方案:For循环是线程安全的,foreach是线程不安全的.说起开好像很高大上哈.意思是在循环内如,如果调用他们自己的循环体.前者是可以的,但是后者是不行的. 再者如果你循环的是字典.字典是键值对的形式,所以采用线程安全的字典ConcurrentDictionary的字典也可以一定程度的解决问题.但是做好的方案还是添加锁 1,

foreach循环语句

一.foreach循环语句介绍: 1.格式: for(类型 变量名称:被遍历数组或集合){ 其他操作(输出操作) } 2.作用: 主要是用于简化书写 二.foreach循环语句遍历数组: public class oneHundredAndEight_oneHundredAndNine { public static void main(String[] args) { String[] names = {"萌萌","纯纯","爱爱","

迭代器、foreach循环、泛型集合

集合的迭代 语法:Iterator<Object> it=集合.iterator(); while(it.hasNext()){ Object obj=it.next(); } is.hasNext(); //判断是否有下一个元素 it.next(); //移动指针,返回指针指向元素 注意:集合在遍历的过程中不能进行修改,如果进行了修改操作,那么就会抛出ConcurrentModificationException异常, 如果需要进行删除,使用迭代器的it.remove()方法 foreach

第46条:for-each循环优先于传统的for循环

for-each循环通过完全隐藏迭代器或者索引变量,避免混乱和出错的可能,适用于集合和数组和任何实现Iterable接口的对象. 使用传统for循环,容易出错: enum Face { ONE, TWO, THREE, FOUR, FIVE, SIX } Collection<Face> faces = Arrays.asList(Face.values()); for(Iterator<Face> i = faces.iterator(); i.hasNext();) for(I

for foreach循环

//for循环 int[] nums = {1,2,3,4,5,6,8,7,9,10 }; for (int i = 0; i < nums.Length;i++ ) { Console.WriteLine(nums[i]); } Console.ReadKey(); //foreach循环 int[] nums = { 1, 2, 3, 4, 5, 6, 8, 7, 9, 10 }; foreach (var item in nums) { Console.WriteLine(item); }

For-Each循环

•For-Each循环的加入简化了集合的遍历 •其語法如下 –for(type element : array) { System.out.println(element).... } •参见程序 ForTest.java 当遍历集合或数组时,如果需要访问集合或数组的下标,那么最好使用旧式的方式来实现循环或遍历,而不要使用增强的for循环,因为它丢失了下标信息. 1 import java.util.ArrayList; 2 import java.util.Collection; 3 impo

JAVA中的for-each循环与迭代

在学习java中的collection时注意到,collection层次的根接口Collection实现了Iterable<T>接口(位于java.lang包中),实现这个接口允许对象成为 "foreach" 语句的目标,而此接口中的唯一方法,实现的就是返回一个在一组 T 类型的元素上进行迭代的迭代器. 一.迭代器Iterator 接口:Iterator<T> 1 public interface Iterator<E>{ 2 3 boolean h