第2章 LINQ C#之LINQ .Net CoreLINQ C#Lambda表达式

2.1 LINQ简介

.NET Core中的LINQ是用来简化数据查询到的技术,使用LINQ技术可以用简洁的语句实现复杂的数据查询。LINQ不仅可以对.NET集合进行查询,在EF Core框架中也广泛应用,因此LINQ是NET人的必会之术。

2.2 Lambda表达式

应用LINQ技术离不开Lambda表达式,Lambda表达式的作用:偷懒,对偷懒,用Lambda可以在实际编程中大大的减少代码量,在“偷懒”的同时,也给代码的阅读带来了很大的便捷性。

2.2.1 委托

提到Lambda表达式,又不得不谈到委托,嘿嘿有点套娃的意思,不急,一步一步来。

委托是一种指向方法的类型,可以理解为函数的指针。下面演示一下委托的基本用法。

public  class Program
{
    //声明委托
    delegate string MyDelegate(int i);
    static void Main(string[]args)
    {
        //定义委托变量并绑定方法
        MyDelegate mDelegate = SayHello;
        //调用委托
        Console.WriteLine(mDelegate(666));
        mDelegate = SayBye;
        Console.WriteLine(mDelegate(999));

    }
    static string SayHello(int i)
    {
        return i+"Hello World!";
    }
    static string SayBye(int i)
    {
        return i + "Bye Bye!";
    }

}

执行结果

a7154f6a4c344f31923a3a0f778a2812.png

除此之外,.NET中定义了多参数的泛型委托Action(无返回值)和Func(有返回值)关于委托的详细解析,可以翻阅微软官方文档委托 - Visual Basic | Microsoft Learn或查看本农自学委托笔记.NETCore委托,C#委托的用法-CSDN博客

2.2.2 匿名方法

委托变量不仅可以指向普通方法,也可以指向匿名方法:

 Func<int, int, int> funcDelegate = delegate (int i1, int i2) {
     return i1 + i2;
 };

2.2.3 Lambda表达式 

将2.2.2中的匿名方法可以用Lambda表达式改写:

 Func<int, int, int> funcDelegate1 =  ( i1,  i2)=>
 {
     return i1 + i2;
 };

与匿名方法不同的是,在Lambda表达式中省略了关键字delegate 和入参的数据类型,这里编译器通过委托的数据类型自动推断出方法i1和i2的数据类型,用=>作为定义方法体的关键字

2.2.3 Lambda表达式的其他形式

(1)如果一个方法体只有一行代码,则可以省略方法体的花括号,例如:

Func<int, int,int> funcDelegate2 = (i1, i2) => i1 + i2;

(2)如果一个方法体只有一个参数,那么参数列表的圆括号也可以省略,例如:

 Func<int,  int> funcDelegate3 = i1 => i1 *10;

(3)Lambda 表达式使用示例(原著示例)

 static void Main(string[] args)
 {
     int[] arrays = {40,10,20,30,50,60,52,45 };
    var returnResult= Select(arrays,n=>n>30);//筛选大于30的数
     var returnResult1 = Select(arrays, n =>n%2==0);//筛选偶数
 }

 static IEnumerable<int> Select(IEnumerable<int> nums,Func<int ,bool> filter)
 {
     foreach (int i in nums)
     {
         if (filter(i)) yield return i;
     }
 }

如果用匿名方法:

 Func<int ,bool> f1 = delegate (int n) {

     return n > 30;
 };
 var returnResult2 = Select(arrays, f1);

由此可见Lambda表达式的优势。

2.3 LINQ常用集合类的扩展方法

LINQ(Language Integrated Query)是C#中的一种查询语言,允许对集合(如数组、列表等)进行数据操作。通过使用LINQ,可以使用类似于SQL的语法来查询和操作数据。

LINQ的关键功能是提供了集合类的扩展方法,所有实现了IEnumberable<T>接口的类都可以使用这些方法。这些方法以扩展方法的形式存在于System.Linq命名空间的静态类中。

在示例讲解前,准备一些初始数据,并添加到List<Employee>

 public class Employee
 {
     /// <summary>
     /// 
     /// </summary>
     /// <param name="id">主键</param>
     /// <param name="Name">姓名</param>
     /// <param name="age">年龄</param>
     /// <param name="gender">性别</param>
     /// <param name="salary">工资</param>
     public Employee(int  id, string Name, int age, bool gender, int salary)
     {
         this.ID = id;
         this.Name = Name;
         this.Age = age;
         this.Gender = gender;
         this.Salary = salary;

     }
     /// <summary>
     /// 主键
     /// </summary>
     public int ID;
     /// <summary>
     /// 姓名
     /// </summary>
     public string Name;
     /// <summary>
     /// 年龄
     /// </summary>
     public int Age;
     /// <summary>
     /// true 男 false 女
     /// </summary>
     public bool Gender;
     /// <summary>
     /// 工资
     /// </summary>
     public int Salary;
 }
 List<Employee> employeeList = new List<Employee>();
 employeeList.Add(new Employee(1, "张三", 52, true, 8000));
 employeeList.Add(new Employee(2, "李四", 34, false, 6000));
 employeeList.Add(new Employee(3, "王五", 22, true, 3200));
 employeeList.Add(new Employee(4, "牛七", 65, false, 4500));
 employeeList.Add(new Employee(5, "马八", 12, true, 800));
 employeeList.Add(new Employee(6, "不是", 7, false, 15000));
 employeeList.Add(new Employee(7, "所有", 88, true, 6300));
 employeeList.Add(new Employee(8, "虫子", 99, false, 4000));
 employeeList.Add(new Employee(9, "都能", 65, true, 10000));
 employeeList.Add(new Employee(10, "变成", 32, false, 4600));
 employeeList.Add(new Employee(11, "蝴蝶", 45, true, 5300));
 employeeList.Add(new Employee(12, "因为", 76, false, 7050));
 employeeList.Add(new Employee(13, "有的", 89, true, 13000));
 employeeList.Add(new Employee(14, "是她", 55, false, 16000));
 employeeList.Add(new Employee(15, "娘的", 48, true, 20000));
 employeeList.Add(new Employee(16, "蛆蛆", 28, false, 18000));

2.3.1 数据过滤:Where方法

Where方法用于根据条件进行过滤数据,声明如下

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)

该方法的predicate参数是一个参数为元素类型,返回值bool的委托。source集合的所有元素经过predicate条件的筛选,所有符合条件的元素会迭代成一个集合,作为方法的返回值。

下面示例一下用法:筛选出年龄大于30且工资大于10000的员工

IEnumerable<Employee> resultList = employeeList.Where(emp => emp.Age > 30 && emp.Salary > 10000);
foreach (var item in resultList)
{
    Console.WriteLine($"{item.Name} {item.Age} {item.Salary} ");
}

执行结果

有的 89 13000
是她 55 16000
娘的 48 20000

2.3.2 Count获取数据条数

Count方法用于获取数据条数,有两个重载,一个是获取查询条件后的集合的数据条数,一个是直接获取条数。

直接获取:

int count= employeeList.Count();

条件获取:

 //有两种写法
 int  whereCount = employeeList.Where(emp => emp.Age > 30 && emp.Salary > 10000).Count();
 int whereCount1 = employeeList.Count(emp => emp.Age > 30 && emp.Salary > 10000);

2.3.3 Any方法

用于判断是否至少有一条满足条件的数据。有两个重载,一个有条件参数,另一个没有。

无参数:

 bool reBool = employeeList.Any();

有参数:

//有两种写法
bool reBool1 = employeeList.Any(emp => emp.Age > 30 && emp.Salary > 10000);
bool reBool12 = employeeList.Where(emp => emp.Age > 30 && emp.Salary > 10000).Any();

总结:Any只要遇到一个符合条件的数据就停止检查后续数据并返回结果,而Count检查所有数据,因此判断数据的存在与否Any方法比Count方法高效。

2.3.4 获取一条数据

有四组方法:Single、SingleOrDefault、First、FirstOrDefault

Single:如果确认有且只有一条满足要求的数据,那么就用 Single 方法。如果没有满足条件的数据,或者满足条件的数据多于一条,Single 方法就会抛出异常。
SingleOrDefault:如果确认最多只有一条满足要求的数据,那么就用 SingleOrDefault方法。如果没有满足条件的数据,SingleOrDefault 方法就会返回类型的默认值。如果满足条件的数据多于一条,SingleOrDefault 方法就会抛出异常。
First:如果满足条件的数据有一条或者多条,First 方法就会返回第一条数据:如果没有满足条件的数据,First 方法就会抛出异常。
FirstOrDefault:如果满足条件的数据有一条或者多条,FirstOrDefault 方法就会返回第一条数据:如果没有满足条件的数据,FirstOrDefault 方法就会返回类型的默认值。

使用示例:

Employee e1 = employeeList.Single(e=>e.ID==5);
Console.WriteLine($"{e1.ID } {e1.Name} {e1.Age} {e1.Salary} ");
Employee e2 = employeeList.SingleOrDefault(e =>e.Age==6 );
if (e2 == null)
    Console.WriteLine("不存在年龄为6的员工");
Employee e3 = employeeList.First(e => e.ID == 7);
Console.WriteLine($"{e3.ID} {e3.Name} {e3.Age} {e3.Salary} ");
Employee e4 = employeeList.FirstOrDefault(e => e.Age  == 8);
if (e4 == null)
    Console.WriteLine("不存在年龄为8的员工");

执行结果:

5 马八 12 800
不存在年龄为6的员工
7 所有 88 6300
不存在年龄为8的员工

2.3.5 排序OrderBy、OrderByDescending

OrderBy正向排序,将所有员工按照年龄排序:

IEnumerable<Employee> orderbyList = employeeList.OrderBy(e => e.Age);
foreach (Employee item in orderbyList)
{
    Console.WriteLine($"{item.ID}\t{item.Name}\t{item.Age}\t{item.Salary}");
}

执行结果

6       不是    7       15000
5       马八    12      800
3       王五    22      3200
16      蛆蛆    28      18000
10      变成    32      4600
2       李四    34      6000
11      蝴蝶    45      5300
15      娘的    48      20000
1       张三    52      8000
14      是她    55      16000
4       牛七    65      4500
9       都能    65      10000
12      因为    76      7050
7       所有    88      6300
13      有的    89      13000
8       虫子    99      4000

OrderByDescending逆向排序,所有员工安装工资逆向排序

Console.WriteLine("逆向排序:");
IEnumerable<Employee> orderbyDesList = employeeList.OrderByDescending(e => e.Salary);
foreach (Employee item in orderbyDesList)
{
    Console.WriteLine($"{item.ID}\t{item.Name}\t{item.Age}\t{item.Salary}");
}

执行结果:

逆向排序:
15      娘的    48      20000
16      蛆蛆    28      18000
14      是她    55      16000
6       不是    7       15000
13      有的    89      13000
9       都能    65      10000
1       张三    52      8000
12      因为    76      7050
7       所有    88      6300
2       李四    34      6000
11      蝴蝶    45      5300
10      变成    32      4600
4       牛七    65      4500
8       虫子    99      4000
3       王五    22      3200
5       马八    12      800

2.3.6 限制结果集

限制结果集合用于从集合中获取部分数据,主要应用场景是分页查询,有两个方法:Skip Take

举例1:从第4条数据开始,获取后面5条数据

Console.WriteLine("限制结果:");
IEnumerable<Employee> skipTakeesList = employeeList.Skip(4).Take(5);
foreach (Employee item in skipTakeesList)
{
    Console.WriteLine($"{item.ID}\t{item.Name}\t{item.Age}\t{item.Salary}");
}

执行结果:

限制结果:
5       马八    12      800
6       不是    7       15000
7       所有    88      6300
8       虫子    99      4000
9       都能    65      10000

举例2:Skip和Take也可以单独使用,比如Skip可以从第n条数据开始获取后面所有数据,即跳过前n个数据,例如

Console.WriteLine("Skip限制结果:");
IEnumerable<Employee> skipList = employeeList.Skip(10);
foreach (Employee item in skipList)
{
    Console.WriteLine($"{item.ID}\t{item.Name}\t{item.Age}\t{item.Salary}");
}

执行结果:

Skip限制结果:
11      蝴蝶    45      5300
12      因为    76      7050
13      有的    89      13000
14      是她    55      16000
15      娘的    48      20000
16      蛆蛆    28      18000

举例3:Take单独使用,获取前3条数据:

Console.WriteLine("Take限制结果:");
IEnumerable<Employee> takeList = employeeList.Take(3);
foreach (Employee item in takeList)
{
    Console.WriteLine($"{item.ID}\t{item.Name}\t{item.Age}\t{item.Salary}");
}

执行结果:

Take限制结果:
1       张三    52      8000
2       李四    34      6000
3       王五    22      3200

2.3.7 聚合函数

如SQL语句那样,LINQ中也有聚合函数:Max、Min、Average、Sum和Count等,可以和Where、Skip、Take等方法一起使用。

Max:获取工资最高值

Console.WriteLine("Max:");
int maxSalary = employeeList.Max(e => e.Salary);
Console.WriteLine($"最高工资:{maxSalary}");

执行结果:

Max:
最高工资:20000

Min:获取年龄最小

Console.WriteLine("Min:");
int minAge = employeeList.Min (e => e.Age );
Console.WriteLine($"最小年龄:{minAge}");

执行结果:

Min:
最小年龄:7

Average:计算平均年龄:

Console.WriteLine("Average:");
double  avgAge = employeeList.Average(e => e.Age);
Console.WriteLine($"平均年龄:{avgAge}");

执行结果:

Average:
平均年龄:51.0625

Sum:计算所有员工工资总和:

Console.WriteLine("Sum:");
int sumSalary = employeeList.Sum(e => e.Salary);
Console.WriteLine($"工资总和:{sumSalary}");

执行结果:

Sum:
工资总和:141750

Count,获取数据条数,前文已经示例过了这里不再赘述。

2.3.8 分组GroupBy

LINQ中用GroupBy方法进行分组,声明如下:

public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)

参数列表中keySelector是分组的条件表达式,返回值为IGrouping<TKey, TSource>,其中TKey表示这组数据的数据项,例如按照性别分组,这个参数就是‘男’或‘女’,IGrouping继承自IEnumerable,因此可以用MinMaxCount等方法进行组内聚合运算。

示例:根据性别进行分组,然后计算组内人数、平均年龄、平均工资等

 Console.WriteLine("分组运算:");
 IEnumerable<IGrouping<bool, Employee>> groupByList = employeeList.GroupBy(e => e.Gender);
 foreach (var item in groupByList)
 {
     Func<bool, string> g = t =>
     {
         if (t == true)
         {

             return "男";
         }
         else
         {
             return "女";
         }
     };
     Console.WriteLine($"性别为{g(item.Key)}的人数:\t{item.Count()}");
     Console.WriteLine($"性别为{g(item.Key)}的平均工资:\t{item.Average(e => e.Salary)}");
     Console.WriteLine($"性别为{g(item.Key)}的平均年龄:\t{item.Average(e => e.Age)}");
 }

执行结果

分组运算:
性别为男的人数:        8
性别为男的平均工资:    8325
性别为男的平均年龄:    52.625
性别为女的人数:        8
性别为女的平均工资:    9393.75
性别为女的平均年龄:    49.5

2.3.9 投影Select

Select方法:把集合中的每一项转换为另一种类型

举例1:提取成员年龄数据列

Console.WriteLine("年龄数据选择:");
IEnumerable<int> Ages = employeeList.Select(e=>e.Age);
foreach (var item in Ages)
{
    Console.Write($"{item}  ");
}

执行结果

年龄数据选择:
52  34  22  65  12  7  88  99  65  32  45  76  89  55  48  28

举例2:选择性别数据列

  Console.WriteLine("性别数据选择:");
  IEnumerable<string > genders = employeeList.Select(e => e.Gender ? "男":"女");
  foreach (var item in genders)
  {
      Console.Write($"{item}  ");
  }

执行结果:

性别数据选择:
男  女  男  女  男  女  男  女  男  女  男  女  男  女  男  女

2.3.10 集合转换ToArray ToList

LINQ的扩展方法大多都是返回IEnumerable<T>类型,可以根据编码需求将其转换成List<T>类型例如选取男性员工:

 Console.WriteLine("选择男性员工:");
 IEnumerable<Employee> wEmployee = employeeList.Where(e => e.Gender==true );
 foreach (var item in wEmployee)
 {
     Console.WriteLine($"{item.ID } \t{item.Name} ");
 }

执行结果:

选择男性员工:
1       张三
3       王五
5       马八
7       所有
9       都能
11      蝴蝶
13      有的
15      娘的

 

2.4 LINQ的另一种写法

LINQ的应用中,不仅可以用方法语法,也可以用“查询语法”进行编码,例如:

方法语法:

 var funcResult = employeeList.Where(e=>e.Salary>10000).OrderBy(e=>e.Age).Select(e=>new { e.Name,e.Age, gen = e.Gender?"男":"女"});

查询语法:

var sqlResult = from e in employeeList
                where e.Salary > 10000
                orderby e.Age
                select new { e.Name, e.Age, gen = e.Gender ? "男" : "女" };

总结:

“查询语法”看起来更新颖,而且比“方法语法”需要写的代码会少一些,但是在编写复杂的查询条件的时候,用“方法语法”编写的代码会更清晰。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值