C# LINQ
视频链接:.NET 6教程,.Net Core 2022视频教程,杨中科主讲_哔哩哔哩_bilibili
Lambda 与 LINQ#
为啥要学LINQ?#
让数据处理变得简单。
【复习】委托
1、委托是可以指向方法的类型,调用委托变量时执行的就是变量指向的方法。
2、.NET 中定义了泛型委托Action(无返回值)和Func(有返回值),所以一般不用自定义委托类型。
Lambda是怎么来的?#
委托变量不仅可以指向普通方法,还可以指向匿名方法。
static void Main(String[] args) { Func<int, int, int> func = delegate (int x, int y) { return x + y; }; Console.WriteLine(func(1, 2)); Action<string> action = delegate (string s) { Console.WriteLine(s); }; action("Hello"); }
匿名方法可以写成Lambda表达式。
可以省略参数数据类型,因为编译器能根据委托类型推断出参数的类型,用=>引出来方法体。
如果=>之后的方法体中只有一行代码,且方法有返回值,那么可以省略方法体的{}以及return。
如果只有一个参数,参数的(可以省略)。
如果委托没有返回值,且方法体只有一行代码,可省略{}
static void Main(String[] args) { Action<string> action = (string s) => { Console.WriteLine(s); }; action("World"); Action<string> action2 = s => { Console.WriteLine(s); }; action2("World"); Action<string> action3 = s => Console.WriteLine(s); action3("World"); }
探秘LINQ方法的背后#
LINQ中提供了很多集合的扩展方法,配合lambda能简化数据处理。
static void Main(String[] args) { int[] nums = { 11, 23, 6, 332, 1521, 44, 2 }; // Where方法会遍历集合中每个元素, // 对于每个元素都调用i=>i>100这个Lambda表达式判断一下是否为true, // 如果为true,则把这个元素放到返回的集合中 IEnumerable<int> result = nums.Where(i => i > 100); // 自定义方法实现where这个扩展方法的功能 //IEnumerable<int> result = MyWhere(nums, i => i > 100); //IEnumerable<int> result = MyWhere2(nums, i => i > 100); foreach (int i in result) { Console.WriteLine(i); } } static IEnumerable<int> MyWhere(IEnumerable<int> nums,Func<int,bool> func) { List<int> result = new List<int>(); foreach (int i in nums) { if (func(i)) { result.Add(i); } } return result; } static IEnumerable<int> MyWhere2(IEnumerable<int> nums, Func<int, bool> func) { foreach (int i in nums) { if (func(i)) { yield return i; } } }
可以使用var让编译器的“类型推断”来简化类型的声明。在LINQ中常用。
C#的var和JavaScript的var不一样,仍然是强类型的。C#中的弱类型是dynamic
常用扩展方法#
常用扩展方法一:Where、Count、Any方法#
LINQ中提供了大量类似Where的扩展方法,简化数据处理。大部分都在System.Linq命名空间中。
(1)
Where方法:每一项数据都会经过predicate的测试,如果针对一个元素, predicate执行的返回值为true,那么这个元素就会放到返回值中。
Where参数是一个lambda表达式格式的匿名方法,方法的参数e表示当前判断的元素对象。参数的名字不一定非要叫e,不过一般lambda表达式中的变量名长度都不长。
(2)
Count方法:获取数据条数
(3)
Any()方法:是否至少有一条数据【有可能效率比Count高】
static void Main(String[] args) { List<Employee> list = new List<Employee>(); list.Add(new Employee(1, "zhangsanA", 20, true, 5000)); list.Add(new Employee(2, "zhangsanB", 26, false, 4000)); list.Add(new Employee(3, "zhangsanC", 31, true, 8000)); list.Add(new Employee(4, "zhangsanD", 35, false, 2000)); list.Add(new Employee(5, "zhangsanE", 43, true, 5000)); list.Add(new Employee(6, "zhangsanF", 35, true, 8000)); IEnumerable<Employee> items1= list.Where(e => e.Age > 30); foreach(var i in items1) { Console.WriteLine(i); } Console.WriteLine(list.Count()); Console.WriteLine(list.Count(e => e.Age > 30)); Console.WriteLine(list.Count(e => e.Age > 30 && e.Salary > 5000)); Console.WriteLine(list.Any(e => e.Age == 20)); Console.WriteLine(list.Any(e => e.Salary > 8000)); }
常用扩展方法二#
获取一条数据的方法 Single、SingleOrDefault、First、FirstOrDefault#
获取一条数据(是否带参数的两种写法):
(1)Single:有且只有一条满足要求的数据;
(2)SingleOrDefault:最多只有一条满足要求的数据;
(3)First:至少有一条,返回第一条【一个都没有,会出错】;
(4)FirstOrDefault:返回第一条或者默认值;
选择合适的方法,“防御性编程”
static void Main(String[] args) { List<Employee> list = new List<Employee>(); list.Add(new Employee(1, "zhangsanA", 20, true, 5000)); list.Add(new Employee(2, "zhangsanB", 26, false, 4000)); list.Add(new Employee(3, "zhangsanC", 31, true, 8000)); list.Add(new Employee(4, "zhangsanD", 35, false, 2000)); list.Add(new Employee(5, "zhangsanE", 43, true, 5000)); list.Add(new Employee(6, "zhangsanF", 35, true, 8000)); // 测试返回一条数据的扩展方法 //list.Single();// 有多条数据,会抛出异常 Employee e1 = list.Where(e => e.Name == "zhangsanA").Single(); Console.WriteLine(e1); IEnumerable<Employee> eitems = list.Where(e => e.Name == "zhangsanA"); Employee e2 = eitems.Single(); Console.WriteLine(e2); var e3 = list.Single(e => e.Name == "zhangsanA");// Single里面也能写条件 Console.WriteLine(e3); var e4 = list.Where(e => e.Name == "lisi").Single();// 如果没有一条数据,也会抛出异常 Console.WriteLine(e4); }
static void Main(String[] args) { int[] nums2 = { 3, 5, 7 }; int i = nums2.SingleOrDefault(e => e > 5); Console.WriteLine(i); //0 没有一条数据满足,返回默认值 int i2= nums2.SingleOrDefault(i => i > 8); Console.WriteLine(i2); // 如果有多条数据,会抛出异常 int i3 = nums2.SingleOrDefault(e => e > 4); Console.WriteLine(i3); }
排序 Order、OrderByDescending#
(1)Order():对数据正序排序;
(2)OrderByDescending()倒序排序;
list.OrderBy(e => e.Age);
对于简单类型排序,也许不用lambda表达式。特殊案例:按照最后一个字排序;用Guid或随机数讲行随机排序。
static void Main(String[] args) { List<Employee> list = new List<Employee>(); list.Add(new Employee(1, "zhangsanA", 20, true, 5000)); list.Add(new Employee(2, "zhangsanB", 26, false, 4000)); list.Add(new Employee(3, "zhangsanC", 31, true, 8000)); list.Add(new Employee(4, "zhangsanD", 35, false, 2000)); list.Add(new Employee(5, "zhangsanE", 43, true, 5000)); list.Add(new Employee(6, "zhangsanF", 35, true, 8000)); IEnumerable<Employee> employees = list.OrderBy(e => e.Salary);//升序 //IEnumerable<Employee> employees = list.OrderByDescending(e => e.Salary);//降序 foreach (Employee employee in employees) { Console.WriteLine(employee); } }
多规则排序:
可以在Order()、OrderByDescending()后继续写ThenBy()、ThenByDescending()
案例:优先按照Age排序,如果Age相同再按照Salary排序
list.OrderBy(e => e.Age).ThenByDescending(e=>e.Salary)
限制结果集,获取部分数据 Skip(n)、Take(n)#
Skip(n)跳过n条数据,Take(n)获取n条数据。
案例:获取从第2条开始获取3条数据 var orderedltems1 = list.Skip(2).Take(3);
Skip()、Take()也可以单独使用。
static void Main(String[] args) { List<Employee> list = new List<Employee>(); list.Add(new Employee(1, "zhangsanA", 20, true, 5000)); list.Add(new Employee(2, "zhangsanB", 26, false, 4000)); list.Add(new Employee(3, "zhangsanC", 31, true, 8000)); list.Add(new Employee(4, "zhangsanD", 35, false, 2000)); list.Add(new Employee(5, "zhangsanE", 43, true, 5000)); list.Add(new Employee(6, "zhangsanF", 35, true, 8000)); IEnumerable<Employee> employees = list.Skip(3).Take(2); foreach (Employee employee in employees) { Console.WriteLine(employee); } }
常用扩展方法三#
聚合函数 Max()、Min()、Average()、Sum()、Count()#
分组 GroupBy()#
GroupBy方法参数是分组条件表达式,返回值为IGrouping<TKey,TSource>类型的泛型lEnumerable,也就是每一组以一个lGrouping对象的形式返回。
IGrouping是一个继承自IEnumerable的接口,lGrouping中Key属性表示这一组的分组数据的值。
static void Main(String[] args) { List<Employee> list = new List<Employee>(); list.Add(new Employee(1, "zhangsanA", 20, true, 5000)); list.Add(new Employee(2, "zhangsanB", 26, false, 4000)); list.Add(new Employee(3, "zhangsanC", 31, true, 8000)); list.Add(new Employee(4, "zhangsanD", 35, false, 2000)); list.Add(new Employee(5, "zhangsanE", 43, true, 5000)); list.Add(new Employee(6, "zhangsanF", 35, true, 8000)); // select Salary,Max(age) from t group by Salary; IEnumerable<IGrouping<int,Employee>> items= list.GroupBy(e => e.Salary); foreach (IGrouping<int, Employee> item in items) { Console.WriteLine(item.Key); Console.WriteLine("最大年龄:" + item.Max(e => e.Age)); foreach (Employee emp in item) { Console.WriteLine(emp); } Console.WriteLine("**********************"); } Console.WriteLine("**********************"); // 示例2:根据年龄分组,获取每组的总人数、最高工资、平均工资 var items2 = list.GroupBy(e => e.Age); foreach (var emp in items2) { Console.WriteLine("总人数:" + emp.Count()); Console.WriteLine("最高工资:" + emp.Max(e => e.Salary)); Console.WriteLine("平均工资:" + emp.Average(e => e.Salary)); } }
常用扩展方法四#
投影 Select()#
把集合中的每一项(逐个的)转换为另外一种类型。
static void Main(String[] args) { List<Employee> list = new List<Employee>(); list.Add(new Employee(1, "zhangsanA", 20, true, 5000)); list.Add(new Employee(2, "zhangsanB", 26, false, 4000)); list.Add(new Employee(3, "zhangsanC", 31, true, 8000)); list.Add(new Employee(4, "zhangsanD", 35, false, 2000)); list.Add(new Employee(5, "zhangsanE", 43, true, 5000)); list.Add(new Employee(6, "zhangsanF", 35, true, 8000)); IEnumerable<int> ints =list.Select(e => e.Salary); foreach (int i in ints) { Console.WriteLine(i); } IEnumerable<string> strs = list.Where(e=>e.Gender==true).Select(e => e.Name); foreach (string i in strs) { Console.WriteLine(i); } IEnumerable<string> strs2= list.Select(e => e.Gender ? "男" : "女"); foreach (string i in strs2) { Console.WriteLine(i); } }
投影与匿名类型
static void Main(String[] args) { List<Employee> list = new List<Employee>(); list.Add(new Employee(1, "zhangsanA", 20, true, 5000)); list.Add(new Employee(2, "zhangsanB", 26, false, 4000)); list.Add(new Employee(3, "zhangsanC", 31, true, 8000)); list.Add(new Employee(4, "zhangsanD", 35, false, 2000)); list.Add(new Employee(5, "zhangsanE", 43, true, 5000)); list.Add(new Employee(6, "zhangsanF", 35, true, 8000)); var items= list.Select(e => new { Xingming = e.Name, Nianling = e.Age, Xingbie = e.Gender ? "男" : "女" }); foreach(var i in items) { Console.WriteLine(i.Xingming + ":" + i.Xingbie); } }
链式调用#
集合转换#
有一些地方需要数组类型或者List类型的变量,我们可以用ToArray()方法和ToList()分别把IEnumerable
链式调用#
Where、Select、OrderBy、GroupBy、Take、Skip等返回值都是IEnumerable
LINQ另外一种倩影——查询语法#
使用Where、OrderBy、Select等扩展方法进行数据查询的写法叫做”LINQ方法语法“。
还有一种”查询语法“的写法。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix