• .Net学习——Linq常用拓展方法使用


    本文主要演示Linq常用拓展方法。包括Where(),Single(),SingleOrDefault(),First(),
    FirstOrDefault(),Count(),Any(),OrderBy(),Skip(),Take(),Max(),
    Min(),Sum(),Average(),GroupBy(),Select(),ToList(),ToArray()。学会并掌握常用的Linq常用拓展方法,可以更快的处理数据源,其语法逻辑与SQL类似。

    Linq语法常用于IEnumerable<泛型>这种可遍历的数据,如List<泛型> Array等。其底层逻辑就是遍历每一个元素,用一定的规则来判定元素是否符合标准。这个规则通常就是扩展方法中传入的委托参数。比如IEnumerable Where(this IEnumerable source, Func predicate); 该扩展方法用来筛选一定条件的数据,并返回新的可遍历的元素组。 委托变量predicate 对传入的TSource进行bool条件判断,如果符合规则,则将该元素加入到返回集中

    委托变量通常指向一个用Lamda表达式描述的匿名方法,所以学习Linq之前还需对委托及Lamda表达式有一定的了解!
    如果又不太了解的读友,可以参考我的前一篇文章 委托、委托与lamda表达式的关系

    1 数据源

        public class Employee
        {
            public long Id;
            public string Name;
            public int Age;
            public int Salary;
            public int Gender;
    
            public Employee(long id, string name, int age, int salary, int gender)
            {
                Id = id;
                Name = name;
                Age = age;
                Salary = salary;
                Gender = gender;
            }
    
            public override string ToString()
            {            
                return $"Id:{Id},Name:{Name},Age:{Age},Salary:{Salary},Gender:{Gender}";
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
               List<Employee> list = new List<Employee>();
                list.Add(new Employee(1,"llk",28,4000,1));
                list.Add(new Employee(2, "lom", 28, 7000, 1));
                list.Add(new Employee(3, "lombok", 35, 9000, 0));
    
                list.Add(new Employee(4, "lld", 28, 8200, 0));
                list.Add(new Employee(5, "ssf", 25, 6000, 1));
                list.Add(new Employee(6, "ghj", 45, 12000, 0));
    
    
    
                list.Add(new Employee(7, "xxk", 28, 8200, 0));
                list.Add(new Employee(8, "qm", 25, 6000, 1));
                list.Add(new Employee(9, "fmh", 38, 9000, 0));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2 查询

    2.1Where

    IEnumerable Where(this IEnumerable source, Func predicate);
    对source中的每个元素进行bool条件判断,为true时加入需要返回的集合中

    
               IEnumerable<Employee> items1 = list.Where(a=>a.Age>=35);
                Console.OutputEncoding = Encoding.Unicode;
                foreach (Employee employee in items1) {
                    Console.WriteLine(employee.ToString());
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    当前筛选条件为元素的Age必须不小于35

    Id:3,Name:lombok,Age:35,Salary:9000,Gender:0
    Id:6,Name:ghj,Age:45,Salary:12000,Gender:0
    Id:9,Name:fmh,Age:38,Salary:9000,Gender:0

    2.2 查询一条数据

    1 TSource Single(this IEnumerable source, Func predicate);
    当TSource满足bool条件判断时,返回一条数据。 0或多笔数据符合条件时,会报错
    System.InvalidOperationException: 'Sequence contains more than one matching element'
    System.InvalidOperationException: 'Sequence contains no matching element'

    2 TSource SingleOrDefault(this IEnumerable source, Func predicate);
    当TSource满足bool条件判断时,返回一条数据。 多笔数据符合条件时,会报错。不存在时会根据TSource的数据类型赋默认的值 比如 TSource为int时,不满足条件时SingleOrDefault查询的值将会是int的默认值 0
    3 TSource First(this IEnumerable source, Func predicate);
    当TSource满足bool条件判断时,返回一条数据。 0笔数据符合条件时,会报System.InvalidOperationException: 'Sequence contains no matching element'异常 多笔数据符合时,会选择最先符合条件的的一笔数据
    4 TSource FirstOrDefault(this IEnumerable source, Func predicate);
    当TSource满足bool条件判断时,返回一条数据。 0笔数据符合条件时,会根据TSource的数据类型赋默认的值 ,多笔数据符合时,会选择最先符合条件的一笔数据

                Employee emp = list.Single(i => i.Id == 1);
                Console.WriteLine(emp.ToString());
    
                emp = list.SingleOrDefault(i => i.Id == 0);
                Console.WriteLine(emp == null ? "Null" : emp.ToString());
    
                emp = list.First(i => i.Id>6);
                Console.WriteLine(emp.ToString());
    
    
                emp = list.FirstOrDefault(i => i.Gender == 0);
                Console.WriteLine(emp == null ? "Null" : emp.ToString());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Id:1,Name:llk,Age:28,Salary:4000,Gender:1
    Null
    Id:7,Name:xxk,Age:28,Salary:8200,Gender:0
    Id:3,Name:lombok,Age:35,Salary:9000,Gender:0

    3 计数Count

    Count(this IEnumerable source, Func predicate);
    对source中的每个元素进行bool条件判断,为true时计数

         int allCount = list.Count();
         int Fcount = list.Count(e => e.Gender == 1);
         int Mcount = list.Count(e => e.Gender == 0);
         Console.WriteLine($"员工总数为:{allCount} 男员工共{Mcount} 女员工共{Fcount}");
    
    • 1
    • 2
    • 3
    • 4

    员工总数为:9 男员工共5 女员工共4

    4 判断 Any

    bool All(this IEnumerable source, Func predicate);
    判断source中的元素是否至少有一个满足bool条件判断

       bool flag = list.Any(a => a.Salary > 15000);
       Console.WriteLine($"是否有员工的工资大于15000?{flag}"); //False
       flag = list.Any(a => a.Id==1);
       Console.WriteLine($"是否有员工的工号为1?{flag}"); //True
    
    • 1
    • 2
    • 3
    • 4

    5 排序

    IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector);
    source元素根据给定的TKey来进行升序排
    IOrderedEnumerable OrderByDescending(this IEnumerable source, Func keySelector);
    source元素根据给定的TKey来进行降序排
    IOrderedEnumerable ThenBy(this IOrderedEnumerable source, Func keySelector);
    source元素根据给定的TKey来进行多规则升序排
    IOrderedEnumerable ThenByDescending(this IOrderedEnumerable source, Func keySelector);
    source元素根据给定的TKey来进行多规则降序排

      IEnumerable<Employee> newEmps = list.OrderBy(i => i.Age);
                Console.WriteLine("年龄升序排");
                foreach (Employee employee in newEmps)
                {
                    Console.WriteLine(employee.ToString());
                }
                //source元素根据给定的TKey来进行降序排
                newEmps = list.OrderByDescending(i => i.Salary);
                Console.WriteLine("工资降序排");
                foreach (Employee employee in newEmps)
                {
                    Console.WriteLine(employee.ToString());
                }
    
                //ThenBy 多规则排序
                newEmps = list.OrderByDescending(i => i.Salary).ThenBy(i => i.Age).ThenByDescending(i => i.Gender);
                Console.WriteLine("工资降序排再年龄升序排,性别降序排");
                foreach (Employee employee in newEmps)
                {
                    Console.WriteLine(employee.ToString());
                }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    年龄升序排
    Id:5,Name:ssf,Age:25,Salary:6000,Gender:1
    Id:8,Name:qm,Age:25,Salary:6000,Gender:1
    Id:1,Name:llk,Age:28,Salary:4000,Gender:1
    Id:2,Name:lom,Age:28,Salary:7000,Gender:1
    Id:4,Name:lld,Age:28,Salary:8200,Gender:0
    Id:7,Name:xxk,Age:28,Salary:8200,Gender:0
    Id:3,Name:lombok,Age:35,Salary:9000,Gender:
    Id:9,Name:fmh,Age:38,Salary:9000,Gender:0
    Id:6,Name:ghj,Age:45,Salary:12000,Gender:0
    工资降序排
    Id:6,Name:ghj,Age:45,Salary:12000,Gender:0
    Id:3,Name:lombok,Age:35,Salary:9000,Gender:
    Id:9,Name:fmh,Age:38,Salary:9000,Gender:0
    Id:4,Name:lld,Age:28,Salary:8200,Gender:0
    Id:7,Name:xxk,Age:28,Salary:8200,Gender:0
    Id:2,Name:lom,Age:28,Salary:7000,Gender:1
    Id:5,Name:ssf,Age:25,Salary:6000,Gender:1
    Id:8,Name:qm,Age:25,Salary:6000,Gender:1
    Id:1,Name:llk,Age:28,Salary:4000,Gender:1
    工资降序排再年龄升序排,性别降序排
    Id:6,Name:ghj,Age:45,Salary:12000,Gender:0
    Id:3,Name:lombok,Age:35,Salary:9000,Gender:
    Id:9,Name:fmh,Age:38,Salary:9000,Gender:0
    Id:4,Name:lld,Age:28,Salary:8200,Gender:0
    Id:7,Name:xxk,Age:28,Salary:8200,Gender:0
    Id:2,Name:lom,Age:28,Salary:7000,Gender:1
    Id:5,Name:ssf,Age:25,Salary:6000,Gender:1
    Id:8,Name:qm,Age:25,Salary:6000,Gender:1
    Id:1,Name:llk,Age:28,Salary:4000,Gender:1

    6 限制结果集 Skip和Take

    Skip(n1) 跳过n1条数据,Take(n2) 获得n2条数据
    通常可以用作数据的分页处理,Skip(3).Take(3); 每三条数据为一页,获得第二页的数据

     newEmps = list.OrderByDescending(i => i.Salary).Skip(3).Take(3);
                Console.WriteLine("分页显示");
                foreach (Employee employee in newEmps)
                {
                    Console.WriteLine(employee.ToString());
                }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    分页显示
    Id:4,Name:lld,Age:28,Salary:8200,Gender:0
    Id:7,Name:xxk,Age:28,Salary:8200,Gender:0
    Id:2,Name:lom,Age:28,Salary:7000,Gender:1

    7 聚合函数

                //TResult Max(this IEnumerable source, Func selector);
                //Func selector 对输入元素TSource判断最大值,返回泛型TResult
                int maxAge= list.Max(i=>i.Age);  // 获得最大年龄,Int类型
                string maxName = list.Max(i => i.Name); //获得最大名字,对String 进行比较
    
                Console.WriteLine("员工最大年龄" + maxAge);
                Console.WriteLine("员工最大名字" + maxName);
    
                //TResult Min(this IEnumerable source, Func selector);
                // //Func selector 对输入元素TSource判断最小值,返回泛型TResult
                int minAge = list.Min(i => i.Age);    // 获得最小年龄,Int类型
                Console.WriteLine("员工最小年龄" + minAge);
    
                //double Average(this IEnumerable source, Func selector); 
                //System.Linq命名空间下基本上都是对IEnumerable接口的扩展方法,因此可以链式调用
                //查询Gender=0 女员工的平均工资
                double agvSalay = list.Where(i => i.Gender == 0).Average(i=>i.Salary);
                Console.WriteLine("女员工的平均工资" + agvSalay);
    
                //Gender=1 男性员工的工资大于6000的人数
                int qty = list.Where(i => i.Gender == 1 && i.Salary > 6000).Count();
                Console.WriteLine("男性员工的工资大于6000的人数" + qty);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    员工最大年龄45
    员工最大名字xxk
    员工最小年龄25
    女员工的平均工资9280
    男性员工的工资大于6000的人数1

    8 分组 GroupBy

     // IEnumerable> GroupBy(this IEnumerable source, Func keySelector);
                // Func keySelector 按照TKey 进行分组
                // IGrouping 返回以Tkey为组的TSource集合 TSource的类型与source的类型一致
                IEnumerable<IGrouping<int, Employee>> sources= list.GroupBy(i => i.Age);
                foreach (IGrouping<int, Employee> group in sources) {
                    Console.WriteLine("年龄组为:" + group.Key);
                    foreach (Employee employee in group)
                    {
                        Console.WriteLine(employee.ToString());
                    }
                    Console.WriteLine("最大工资"+group.Max(i=>i.Salary));
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    年龄组为:28
    Id:1,Name:llk,Age:28,Salary:4000,Gender:1
    Id:2,Name:lom,Age:28,Salary:7000,Gender:1
    Id:4,Name:lld,Age:28,Salary:8200,Gender:0
    Id:7,Name:xxk,Age:28,Salary:8200,Gender:0
    最大工资8200
    年龄组为:35
    Id:3,Name:lombok,Age:35,Salary:9000,Gender:0
    最大工资9000
    年龄组为:25
    Id:5,Name:ssf,Age:25,Salary:6000,Gender:1
    Id:8,Name:qm,Age:25,Salary:6000,Gender:1
    最大工资6000
    年龄组为:45
    Id:6,Name:ghj,Age:45,Salary:12000,Gender:0
    最大工资12000
    年龄组为:38
    Id:9,Name:fmh,Age:38,Salary:9000,Gender:0
    最大工资9000

    9 投影 Select

    Enumerable Select(this IEnumerable source, Func selector);
    Func selector 將TSource转换成另一个类型

             var ages =  list.Select(i=>i.Age);
                Console.WriteLine("遍历所有的年龄");
                foreach (int age in ages)
                {
                    Console.WriteLine(age);
                }
                //Employee to String
                var genders = list.Select(i=>i.Gender==1?"男":"女");
                Console.WriteLine("遍历所有的性別");
                foreach (string gender in genders)
                {
                    Console.WriteLine(gender);
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    遍历所有的年龄
    28
    28
    35
    28
    25
    45
    28
    25
    38
    遍历所有的性別








    10 集合转换 ToList和ToArray

    在一定的情形下,需要将IEnumerable 通过ToList转换为List类型 ToArray转换为数组类型

      List <Employee> li = list.ToList();
                Employee[] arr= list.ToArray();
                foreach (Employee e in li)
                {
                    Console.WriteLine(e.ToString());
                }
    
                foreach (Employee e in arr)
                {
                    Console.WriteLine(e.ToString());
                }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    11 链式调用

    System.Linq命名空间下的扩展方法基本上都是对IEnumerable<泛型>的处理,且Where、Select、OrderBy、GroupBy、Take、Skip的返回值都是IEnumerable <泛型>,所有可以进行链式调用去更快速的处理数据

      //分组 投影 聚合函数
       var obj=   list.GroupBy(i=>i.Gender).Select(g=>new {Gender=g.Key==1?"男":"女",Count=g.Count(),MaxSalary=g.Max(item=>item.Salary),AgvSalay=g.Average(item=>item.Salary)});
       foreach(var o in obj)
       {
            Console.WriteLine($"Gender:{o.Gender},MaxSalary:{o.MaxSalary} AgvSalay:{o.AgvSalay}");
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Gender:男,MaxSalary:7000 AgvSalay:5750
    Gender:女,MaxSalary:12000 AgvSalay:9280

  • 相关阅读:
    Python基础入门例程3-NP3 读入字符串
    「Verilog学习笔记」边沿检测
    国内手机安装 Google Play 服务 (GMS/Google Mobile Services)
    test_pipeline
    zabbix自定义模板,邮件报警,代理服务器,自动发现与自动添加及snmp
    产品推荐 - 基于复旦微 JFM7K325T FPGA 的高性能 PCIe 总线数据预处理载板(100%国产化)
    Java多线程同步工具类:Semaphore原理剖析
    线性表--队列-1
    CSRF 002
    Java 第三阶段增强分析需求,代码实现能力【多用户即时通信系统】
  • 原文地址:https://blog.csdn.net/contact97/article/details/127670167