• C# SelectMany 的使用


    刚开始学Linq的时候,学会使用Select就觉得很兴奋。等某天看到SelectMany时,就觉得非常很神奇了,这什么东西,怎样使用法啊。有时候,学习光靠看,是不能理解的,最好是看,理解和实践操作。

    本文借鉴了https://www.xuebuyuan.com/851846.html 的文章,补充部分注释和实例。

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

     
        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,写的都要吐了,简直恨不得做成代码段。

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

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

    如下官方解释:

                  Projects each element of a sequence to an System.Collections.Generic.IEnumerable<T>,
            //     flattens the resulting sequences into one sequence, and invokes a result
            //     selector function on each element therein.

          SelectMany 可以把一个集合中的元素投影到IEnumerable<T>类型的集合中去,然后再合并结果并集到一个集合中.

          SelectMany把Teacher中的Student集合投注到IEnumeralbe<Student>集合中去,注意要想使用SelectMany,对象必须嵌套一个集合类型的对象才可以.所以说SelectMany专门用来替换二层循环也很适当 .      

          可以很方便的处理二重循环问题.

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

    把鼠标放在SelectMany上面会显示如下注释,首先SelecMany会对每一个Teacher执行Func<Teacher,IEnumerable<Student>>委托(Func<Teacher,IEnumerable<Student>>委托是一个用Teacher做为参数并返回一个IEnumerable<Student>集合的委托类型.).接下平合并所有IEnumerable<Student>集合的功能交由SelectMandy内部来完成.它会把

    把有的IEnumerable<Student>合并成一个大的集合.

    public static IEnumerable<Student> SelectMany<Teacher,Student>(
        this IEnumerable<Teacher> source,
        Func<Teacher, IEnumerable<Student>> selector
    原型如下:
    public static IEnumerable<TResult> SelectMany<TSource, TResult>(
        this IEnumerable<TSource> source,
        Func<TSource, IEnumerable<TResult>> selector
    )

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

    再举个例子,把所有学生的成绩按照从高到底排序出来;

    var scores = teachers.SelectMany((t, i) => t.Students.Select(s => s.Score)).OrderByDescending(t => t).ToList();

    scores.ForEach(s => Console.WriteLine(s));


            有时候我们需要输出更复杂的结果集,比如校长想知道教出这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);

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

  • 相关阅读:
    [转]initrd.img, vmlinux
    [转]关于arm 上kernel, qemu起VM等
    [转]overlayFS
    [转]virtiofs
    [转] dynamic DMA mapping
    [转] cpu亲和性
    [转] /dev/shm
    huginn,n8n,ifttt
    ipfs---protocol, filesystem,web p2p,cdn
    gpg,pgp--
  • 原文地址:https://www.cnblogs.com/dusf/p/12205213.html
Copyright © 2020-2023  润新知