• 转载有个小孩跟我说LINQ(重点讲述Linq中GroupBy的原理及用法)


    转载原出处: http://www.cnblogs.com/AaronYang/archive/2013/04/02/2994635.html 

    1  LINQ准备

         新建项目 linq_Ch1控制台程序,新建一个Entity文件夹

        1.1 对象初始化器

             在Entity新建一个类Student,代码如下

       1:  using System;
       2:  using System.Collections.Generic;
       3:  using System.Linq;
       4:  using System.Text;
       5:   
       6:  namespace linq_Ch1.Entity
       7:  {
       8:      public class Student
       9:      {
      10:          /// <summary>
      11:          /// 学生编号
      12:          /// </summary>
      13:          public int Id { get; set; }
      14:          /// <summary>
      15:          /// 学生姓名
      16:          /// </summary>
      17:          public string StudentName { get; set; }
      18:          /// <summary>
      19:          /// 生日
      20:          /// </summary>
      21:          public DateTime Birthday { get; set; }
      22:      }
      23:  }

    在Program.cs文件代码添加如下

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using linq_Ch1.Entity;
     
    namespace linq_Ch1
    {
        class Program
        {
            static void Main(string[] args)
            {
                //1 对象初始化器 2013年4月1日22:14:52
                List<Student> students = new List<Student>();
                students.Add(new Student { Id=1,StudentName="达一",Birthday=DateTime.Now});
                students.Add(new Student { Id = 2, StudentName = "坤二", Birthday = DateTime.Now });
                students.Add(new Student { Id = 3, StudentName = "张三", Birthday = DateTime.Now });
                Console.WriteLine("用对象初始化器方式:"+students[1].StudentName);
                
     
     
                Console.ReadLine();
            }
        }
    }

        1.2 集合初始化器

     //2 集合初始化器 2013年4月1日22:19:06
                List<Student> students_2 = new List<Student> { 
                   new Student { Id=1,StudentName="达一",Birthday=DateTime.Now},
                  new Student { Id = 2, StudentName = "坤二", Birthday = DateTime.Now },
                  new Student { Id = 3, StudentName = "张三", Birthday = DateTime.Now }
                };

    Console.WriteLine("用集合初始化器方式:" + students[1].StudentName);

       1:        //Dictionary版本
       2:              Dictionary<string, Student> student_dic = new Dictionary<string, Student> { 
       3:                {"达一",new Student { Id=1,StudentName="达一",Birthday=DateTime.Now}},
       4:                {"坤二",  new Student { Id = 2, StudentName = "坤二", Birthday = DateTime.Now }},
       5:                {"张三",new Student { Id=3,StudentName="张三",Birthday=DateTime.Now}},
       6:              };
       7:              Console.WriteLine("用集合初始化器方式Dictionary版本:" + student_dic["达一"].StudentName);

         1.3 创建隐身类型的局部变量

           使用var关键字,用is判断类型,不能初始化为null,不能同一语句中初始化多个隐式类型的变量

     //3.使用var关键字,就是不显示声明的类型
                var var_int = 1;
                var var_double = 2.22;
                var var_string = "茗洋芳竹";
                Console.WriteLine("var_int是数字吗?"+(var_int is int));
                Console.WriteLine("var_double是浮点型数字吗?" + (var_double is double));
                Console.WriteLine("var_string是字符串吗?" + (var_string is string));
     //4.使用var保存查询Student的结果
                List<Student> stu = new List<Student>();
                for (int i = 1; i < 10; i++)
                {
                    stu.Add(new Student { Id=i,StudentName="学生"+i.ToString(),Birthday=DateTime.Now});
                }
                var query = from o in stu
                            where o.Id < 5
                            select new
                            {
                                学生编号 = o.Id,
                                学生姓名 = o.StudentName
                            };
                foreach (var item in query)
                {
                    Console.WriteLine(item.学生编号+":"+item.学生姓名);
                }

         1.4创建隐型数组

               ①数组初始化器中的所有元素不能隐式转换为同一类型的数据,就会产生编译错误。

               ②其他

    //创建隐型数组
                var arr1 = new[] {0,1,2,3,4,5,6,7,8,9,10 };
                var arr2 = new[,] { {"1","小A","xa"},{"2","小B","xb"},{"3","小C","xc"}};
                Console.WriteLine(arr1[1]); //1
                Console.WriteLine(arr2[1,1]); //小B

         1.5创建匿名类型对象(很重要)

    //6.创建匿名类型的对象
                var StudentInfo = new { Id=1,StudentName="陈四",Birthday=DateTime.Now};
                Console.WriteLine("生日是:"+StudentInfo.Birthday.ToString("yyyy年MM月dd日"));

        1.6 Lambda表达式(超重要)

           在void main方法上面声明几个委托,如下

                image

              然后添加代码如下:

     
                //7 lambda
                AddOne myAddOne;
                //参数的多少跟委托的签名的参数相等
                myAddOne = x => x + 1;//隐式声明一个参数,表达式方法体
                myAddOne = (int x) => x + 1;//显示声明一个参数,表达式方法体
                ToAdd myToAdd = (x, y) => x + y; //lambda表达式支持用表达式或语句块作为方法体,语法上比匿名方法更加灵活(匿名方法的方法体只能是语句块)
                Method1 myMethod=()=>Console.WriteLine("myToAdd"); //无参数,表达式方法体
                myMethod(); //调用myMethod方法

        1.8 用语句作为Lambda表达式的方法体

    //8.用语句作为Lambda表达式的方法体
                myAddOne = x => { return x + 1; };
                myAddOne = (int x) => { return x + 1; };
                Console.WriteLine("正确答案应该是5:,你的答案是"+myAddOne(4));

      1.9 扩展方法-创建自己的where操作符

           ①扩展Datetime方法

        //扩展方法
        static class ExtendMethod{
            public static string ToYYYYMMDD(this DateTime dt)//自身必须是静态方法,第一个参数必须是this,后面紧跟要扩展的类的名称
            {
                return dt.ToString("yyyyMMdd");
            }
        }

          然后我们在void main方法中调用

    //9 扩展方法
                Console.WriteLine(DateTime.Now.ToYYYYMMDD());
                Console.ReadLine();

             ②创建MyWhere操作符,扩展方法MyWhere的第一个参数类型是IEnumerable<TSource>,表示该方法作用于所有实现IEnumerable<TSource>接口的类。第二个参数是Func<TSource,bool>类型,表示第二个参数接受匿名方法或者Lambda表达式。在该方法中首先使用foreach遍历第一个参数传入的序列,然后逐一判断序列中的元素是否满足第二个参数传入的匿名方法或Lambda的表达式条件

              继续在ExtendMoehtod方法中添加如下代码

      public static IEnumerable<TSource> MyWhere<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
                foreach (TSource s in source)
                {
                    if(predicate(s))
                    yield return s;
                }
            }

    执行foreach循环过程中,每次遇到yield return语句时,该方法都将会向调用者(foreach循环)返回一个值,调用者收到该值后进行相应的处理(如 Console.Write),随后将控制权交回给迭代器方法MyWhere。再由迭代器方法给出下一个元素。yield关键字不需要CLR提供支持,它是由编译器实现的,编译器生成实现迭代器的必要代码。

            调用方法

       //9 扩展方法
                Console.WriteLine("==============扩展方法地盘=============");
                Console.WriteLine(DateTime.Now.ToYYYYMMDD());
                List<int> listint = new List<int> { 0,1,2,3,4,5,6,7,8,9,10};
                var queryint = listint.MyWhere(x=>x%2==0);//取出偶数
                foreach (var item in queryint)
                {
                    Console.WriteLine(item);
                }
                Console.WriteLine("==============扩展方法地盘结束=============");

    目前Program.cs中的代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using linq_Ch1.Entity;
     
    namespace linq_Ch1
    {
        
        class Program
        {
            //初始化一些委托
            public delegate int AddOne(int i);
            public delegate int ToAdd(int i,int j);
            public delegate void Method1();
     
            static void Main(string[] args)
            {
                //1 对象初始化器 2013年4月1日22:14:52
                List<Student> students = new List<Student>();
                students.Add(new Student { Id = 1, StudentName = "达一", Birthday = DateTime.Now });
                students.Add(new Student { Id = 2, StudentName = "坤二", Birthday = DateTime.Now });
                students.Add(new Student { Id = 3, StudentName = "张三", Birthday = DateTime.Now });
                Console.WriteLine("用对象初始化器方式:" + students[1].StudentName);
     
     
                //2 集合初始化器 2013年4月1日22:19:06
                List<Student> students_2 = new List<Student> { 
                   new Student { Id=1,StudentName="达一",Birthday=DateTime.Now},
                  new Student { Id = 2, StudentName = "坤二", Birthday = DateTime.Now },
                  new Student { Id = 3, StudentName = "张三", Birthday = DateTime.Now }
                };
                Console.WriteLine("用集合初始化器方式:" + students[1].StudentName);
     
                //Dictionary版本
                Dictionary<string, Student> student_dic = new Dictionary<string, Student> { 
                  {"达一",new Student { Id=1,StudentName="达一",Birthday=DateTime.Now}},
                  {"坤二",  new Student { Id = 2, StudentName = "坤二", Birthday = DateTime.Now }},
                  {"张三",new Student { Id=3,StudentName="张三",Birthday=DateTime.Now}},
                };
                Console.WriteLine("用集合初始化器方式Dictionary版本:" + student_dic["达一"].StudentName);
     
                //3.使用var关键字,就是不显示声明的类型
                var var_int = 1;
                var var_double = 2.22;
                var var_string = "茗洋芳竹";
                Console.WriteLine("var_int是数字吗?"+(var_int is int));
                Console.WriteLine("var_double是浮点型数字吗?" + (var_double is double));
                Console.WriteLine("var_string是字符串吗?" + (var_string is string));
     
                //4.使用var保存查询Student的结果
                List<Student> stu = new List<Student>();
                for (int i = 1; i < 10; i++)
                {
                    stu.Add(new Student { Id=i,StudentName="学生"+i.ToString(),Birthday=DateTime.Now});
                }
                var query = from o in stu
                            where o.Id < 5
                            select new
                            {
                                学生编号 = o.Id,
                                学生姓名 = o.StudentName
                            };
                foreach (var item in query)
                {
                    Console.WriteLine(item.学生编号+":"+item.学生姓名);
                }
     
                //5创建隐型数组
                var arr1 = new[] {0,1,2,3,4,5,6,7,8,9,10 };
                var arr2 = new[,] { {"1","小A","xa"},{"2","小B","xb"},{"3","小C","xc"}};
                Console.WriteLine(arr1[1]); //1
                Console.WriteLine(arr2[1,1]); //小B
     
     
                //6.创建匿名类型的对象
                var StudentInfo = new { Id=1,StudentName="陈四",Birthday=DateTime.Now};
                Console.WriteLine("生日是:"+StudentInfo.Birthday.ToString("yyyy年MM月dd日"));
     
                
     
                //7 lambda
                AddOne myAddOne;
                //参数的多少跟委托的签名的参数相等
                myAddOne = x => x + 1;//隐式声明一个参数,表达式方法体
                myAddOne = (int x) => x + 1;//显示声明一个参数,表达式方法体
                ToAdd myToAdd = (x, y) => x + y; //lambda表达式支持用表达式或语句块作为方法体,语法上比匿名方法更加灵活(匿名方法的方法体只能是语句块)
                Method1 myMethod=()=>Console.WriteLine("myToAdd"); //无参数,表达式方法体
                myMethod(); //调用myMethod方法
     
                //8.用语句作为Lambda表达式的方法体
                myAddOne = x => { return x + 1; };
                myAddOne = (int x) => { return x + 1; };
                Console.WriteLine("正确答案应该是5:,你的答案是"+myAddOne(4));
     
     
     
                //9 扩展方法
                Console.WriteLine("==============扩展方法地盘=============");
                Console.WriteLine(DateTime.Now.ToYYYYMMDD());
                List<int> listint = new List<int> { 0,1,2,3,4,5,6,7,8,9,10};
                var queryint = listint.MyWhere(x=>x%2==0);//取出偶数
                foreach (var item in queryint)
                {
                    Console.WriteLine(item);
                }
                Console.WriteLine("==============扩展方法地盘结束=============");
     
     
     
                Console.ReadLine();
            }
        }
     
        //扩展方法
        static class ExtendMethod{
            public static string ToYYYYMMDD(this DateTime dt)//自身必须是静态方法,第一个参数必须是this,后面紧跟要扩展的类的名称
            {
                return dt.ToString("yyyyMMdd");
            }
            public static IEnumerable<TSource> MyWhere<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
                foreach (TSource s in source)
                {
                    if(predicate(s))
                    yield return s;
                }
            }
        }
     
    }

    2  LINQ TO Objects查询    

         2.1 简单查询

             说明:一个班级有多个学生,在Entity文件夹下添加ClassRoom.cs文件,代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
     
    namespace linq_Ch1.Entity
    {
        public class ClassRoom
        {
            public int ClassId { get; set; }
            public string ClassName { get; set; }
            List<Student> students = new List<Student>();
        }
    }

             在Program.cs中添加一个方法,用于初始化查询数据

     static List<ClassRoom> BuildClassRoom()
            {
                List<ClassRoom> classroom = new List<ClassRoom>();
                for (int j = 1; j < 4; j++)  //创建3个班级
                {
                    List<Student> stu = new List<Student>();
                    for (int i = 1; i < 10; i++)
                    {
                        stu.Add(new Student { Id = i, StudentName = j + "班的学生" + i, Birthday = DateTime.Now });
                    }
                    classroom.Add(new ClassRoom { ClassId = j, ClassName = j + "班", students = stu });
                }
                return classroom;
            }

    在void Main方法中添加如下代码开始查询

          //初始化数据
                List<ClassRoom> classinfo = BuildClassRoom();
                //查出2班内学生编号小于5的学生信息
                var classQuery = from c in classinfo
                                 from s in c.students
                                 where c.ClassId == 2 && s.Id < 5
                                 select new
                                 {
                                     学生ID = s.Id,
                                     学生姓名 = s.StudentName,
                                     学生班级 = c.ClassName
                                 };
                foreach (var item in classQuery)
                {
                    Console.WriteLine("学生ID="+item.学生ID+",姓名="+item.学生姓名+",所在班级="+item.学生班级);
                }
                Console.ReadLine();
            }

     2.2 应用自定义方法筛选数据

          ①在Program类下添加一个方法

        static bool IsXingWang(string name) {
                return name.IndexOf("王") > -1;
            }

         ②使用方法

          //用自定义的方法筛选数据
                List<Student> students_defined = new List<Student>();
                students_defined.Add(new Student { Id = 1, StudentName = "王一", Birthday = DateTime.Now });
                students_defined.Add(new Student { Id = 2, StudentName = "坤二", Birthday = DateTime.Now });
                students_defined.Add(new Student { Id = 3, StudentName = "张三", Birthday = DateTime.Now });
                //找出含有 王 字的学生信息
                var definedFilter = from s in students_defined
                                    where IsXingWang(s.StudentName)
                                    select s;
                string fmt = "学生编号={0};姓名={1}";
                foreach (var items in definedFilter)
                {
                    Console.WriteLine(string.Format(fmt,items.Id,items.StudentName));
                }

     2.3 将字符串数组按照元素的长度分组(练习group by)

        代码如下

       //练习group by
                string[] words =new string[]{"What","is","your","name","?","my","name","is","mingyang","."};
                var Groups = from word in words
                             group word by word.Length into lengthGroups
                             orderby lengthGroups.Key descending
                             select new
                             {
                                 Length = lengthGroups.Key,
                                 WordCollection = lengthGroups
                             };
                //遍历每组的单词
                foreach (var group in Groups)
                {
                    Console.WriteLine("包含"+group.Length.ToString()+"个字符的单词有");
                    foreach (string word in group.WordCollection)
                    {
                        Console.Write("	" + word);
                    }
                    Console.WriteLine();
                }

    按照字符的长度分组后,每一组看成一个整体,我们把每一组的值通过into关键字保存到临时变量lengthGroups里,那么这个临时变量可以看做是一个整体,里面的内容大致如下

     image

    第二种方法:

           //第二种方法
                var twogroup = words.GroupBy(word=>word.Length);
                foreach (IGrouping<int,string> group in twogroup)
                {
                     Console.WriteLine("包含"+group.Key.ToString()+"个字符的单词有");
                    foreach (string word in group)
                    {
                        Console.Write("	" + word);
                    }
                    Console.WriteLine();
                }

     2.4 获得序列中元素的索引位置

    类似MSSQL数据库ROW_NUMBER()函数的功能

    代码如下

    //获得序列中元素的索引位置
                List<Student> studentsIndex = new List<Student>();
                studentsIndex.Add(new Student { Id = 101, StudentName = "达一", Birthday = DateTime.Now });
                studentsIndex.Add(new Student { Id = 102, StudentName = "坤二", Birthday = DateTime.Now });
                studentsIndex.Add(new Student { Id = 103, StudentName = "张三", Birthday = DateTime.Now });
                var index_string_query = studentsIndex.Select((person, index) => new { index, person.StudentName})
                                                      .OrderBy(i=>i.StudentName);
                foreach (var item in index_string_query)
                {
                    Console.WriteLine(item.index+":"+item.StudentName);
                }
    效果图:
    image
     

     2.5 Orderby (多条件排序)

     方式1:

    //Orderby
                //获得序列中元素的索引位置
                List<Student> studentsOrderby= new List<Student>();
                studentsOrderby.Add(new Student { Id = 104, StudentName = "达一", Birthday = Convert.ToDateTime("1991-04-01") });
                studentsOrderby.Add(new Student { Id = 102, StudentName = "战三", Birthday = Convert.ToDateTime("1991-04-04") });
                studentsOrderby.Add(new Student { Id = 103, StudentName = "坤二", Birthday = Convert.ToDateTime("1991-02-22") });
                //集合带的
                var orderQuery1 = studentsOrderby.OrderBy(p => p.Birthday).ThenBy(p=>p.StudentName); //多条件排序
                foreach (var item in orderQuery1)
                {
                    Console.WriteLine(item.Id+"  "+item.StudentName+"  "+item.Birthday.ToYYYYMMDD());
                }

    方式2:

          var orderQuery2 = from o in studentsOrderby
                                  orderby o.Birthday, o.Id
                                  select o;
                foreach (var item in orderQuery2)
                {
                    Console.WriteLine(item.Id + "  " + item.StudentName + "  " + item.Birthday.ToYYYYMMDD());
                }

     2.6 Reverse,Enumerable类的Reverse方法,反转序列中的元素

          Console.WriteLine("反转后的studentsOrderby");
                studentsOrderby.Reverse();
                foreach (var item in studentsOrderby)
                {
                    Console.WriteLine(item.Id + "  " + item.StudentName + "  " + item.Birthday.ToYYYYMMDD());
                }

       

     2.7 自定义ForEach操作符

       首先添加一个在ExtendMethod类添加一个扩展方法

       1:   //ForEach,每次遍历序列的时候,都将值交给一个自定义的方法去操作和处理
       2:          public static void ForEach<T>(this IEnumerable<T> source,Action<T> func) {
       3:              foreach (var item in source)
       4:              {
       5:                  func(item);
       6:              }
       7:          }

    然后在 void main方法中添加代码

       1:    Console.WriteLine("自定义扩展ForEach");
       2:              //ForEach
       3:              List<Student> studentsForEach= new List<Student>();
       4:              studentsForEach.Add(new Student { Id = 104, StudentName = "达一", Birthday = Convert.ToDateTime("1991-04-01") });
       5:              studentsForEach.Add(new Student { Id = 102, StudentName = "战三", Birthday = Convert.ToDateTime("1991-04-04") });
       6:              studentsForEach.Add(new Student { Id = 103, StudentName = "坤二", Birthday = Convert.ToDateTime("1991-02-22") });
       7:              studentsForEach.ForEach(p => { Console.WriteLine(p.Id+"    "+p.StudentName); });

     2.8  限定符操作

          ①All

             集合.All(lambda表达式)操作,示例代码如下

                //限定符操作
                //All     判断studentsForEach中的所有学生的生日是不是大于>90年的
                bool result=studentsForEach.All(x=>x.Birthday>Convert.ToDateTime("1990-12-31"));
                Console.WriteLine(result);     //True

              用Count判断

    bool result2 = studentsForEach.Count(x => x.Birthday > Convert.ToDateTime("1990-12-31")) > studentsForEach.Count();
                Console.WriteLine(result);     //True

          ②Any

            跟All比较一下,就知道了,我们把All换成Any,意思就是,在studentsForEach集合中是否存在至少一个学生大于90年的

            用集合.Exists(…..)      也可以实现Any效果

          ③Contains最常用的,集合.Contains(值),检查寻列中时候包含指定元素

          

     2.9  元素操作

          ①ElementAt(索引)获得指定位置的元素

    List<int> ints = new List<int> { 1,2,3,4,5,6};
                int intresult = ints.ElementAt(3);     //4      获取指定位置的元素,从0开始
                Console.WriteLine(intresult);

           使用ints.Skip(3).First();也可以达到同样的效果

         ②集合.First() ;获取第一个元素,同样 ElementAt(0)也可以达到同样的效果

         ③集合.Last() ;获取最后一个元素

         ④集合.Where(Lambda表达式),条件筛选出数据。例如 StudentsList.Where(x=>x.Id==1);找出Id等于1的学生对象

         ⑤集合.OrderbyDescending(Lambda表达式),按什么条件降序,Orderby是升序

         ⑥集合.Single(Lambda表达式)方法返回源序列中满足指定条件唯一元素,如果有多个这样的元素存在,则会引发异常

         ⑦集合.SingleOrDefault(Lambda表达式)方法返回源序列中满足指定条件唯一元素,如果有多个这样的元素存在,则会引发异常.如果该序列为空,则返回默认值。

         ⑧集合.DefaultIfEmpty()如果源序列为空,返回只包含一个元素(值为默认值)的序列;如果是集合.DefaultIfEmpty(参数)的

    例如int型的数组ints,代码为 ints.DefaultIfEmpty(-1),则代表如果ints序列为空,则返回-1

         ⑨创建一个空序列 例如 var query=Enumerable.Empty<Student>等同于代码 Student[] s=new Student[0];

         ⑩创建一个指定范围值的数组

           例如: int[] ints=Enumerable.Range(0,10).ToArray();//则数组有10个,分别是0,1,2,3,4,5…,9

                     List<int> ints=Enumerable.Range(0,10).ToList();

         (11).创建一个重复值的数组

        string[] strings2 = Enumerable.Repeat("9", 3).ToArray();
                strings2.ForEach(p => { Console.WriteLine(p); });

         则创建一个数组,等同于 string[] s=new string[]{"9","9","9"};

  • 相关阅读:
    PLSQL Developer调试 存储过程和触发器
    oracle 根据汉字返回拼音函数
    Oracle Length 和 Lengthb 函数说明 .(用来判断记录值里是否有中文内容)
    getline()函数
    自编Photoshop简单教程
    Exception in thread &quot;main&quot; java.lang.UnsupportedClassVersionError: org/apache/ma ven/cli/Maven
    android 动态设置TextView值,例:金额添加
    JavaScript定时调用函数(SetInterval与setTimeout)
    Json数据的序列化与反序列化的三种经常用法介绍
    手动脱FSG壳实战
  • 原文地址:https://www.cnblogs.com/wphl-27/p/3610621.html
Copyright © 2020-2023  润新知