P22:为啥要学LINQ
对比Python :numpy
学习路线
用数据类型定义的理念解释委托
委托:方法的(数据)类型,指向方法的类型,对比 int i=5;整数类型的i指向整数5;数据的类型
D1 d = F1;//注意F1不带括号(),带了表示方法调用了 d();//执行D1所指向的方法 d = F2; d(); D2 d2 = Add; Console.WriteLine(d2(3, 5)); Console.ReadLine(); static void F1() { Console.WriteLine("我是F1"); } static void F2() { Console.WriteLine("我是F2"); } static int Add(int i1,int i2) { return i1 + i2; } delegate void D1();//委托要定义在后面 delegate int D2(int a,int b);//参数名字可以不一样,但是类型必须一样
日常编程中我们几乎不会自己定义委托,C#已经内置了两个泛型委托
最多16个,所以基本够用了
Func<int, int, int> f = Add;//最后一个是返回值类型,类型根据指定的函数来确定 Console.WriteLine(f(3, 5)); Action action = F1; action(); Console.ReadLine();
效果一致
P23:Lambda是怎么来的
匿名方法:没有名字的方法
Action f1 = delegate ()//匿名方法,无参数无返回值 { Console.WriteLine("我是aaa"); }; f1 (); Action<int, int> f2 = delegate (int a, int b)//匿名方法,有参数无返回值 { Console.WriteLine(a + b); }; f2(3, 4); Func<int, int, int> f3 = delegate (int a, int b)//匿名方法,有参数有返回值 { return a * b; }; Console.WriteLine(f3(2, 5)); Console.ReadLine();
=> goes to
去掉delegate,参数列表后面、{前面添加=>符号,参数不再需要指明类型(当然可以指明类型,要指明都指明,不能只指明一部分参数的类型)
Func<int, int, int> f4 = (a, b) =>//匿名方法,Lambda表达式写法 { return a * b; }; Console.WriteLine(f3(3, 5));
Action f5 = () => Console.WriteLine("无返回值,且只有一条语句,可以连{}也省略"); f5();
Func<int, int, int> f42 = (a, b) => a * b; Console.WriteLine(f42(3, 5));
Action<int> f6 = a => Console.WriteLine(a + "只有一个参数"); f6(5);
Func<int, bool> f66 = a => a > 0; Console.WriteLine( f66(55));
P24:揭秘LINQ方法的背后(自己写一个)
int[] num=new int[] {5,8,22,2,5,77,8,552,2,255,412,9}; //Where方法会遍历集合中每个元素,对于每个元素 //都调用a=>a>10这个表达式判断一下是否为true //如果为true,则把这个放到返回的集合中 IEnumerable<int> result=num.Where(x => x > 100);//IEnumerable的扩展方法,需要引用LINQ foreach (var x in result) { Console.WriteLine(x); } Console.WriteLine("--------------自己写一个-------------------"); //自己写一个 static IEnumerable<int> MyWhere(IEnumerable<int> items,Func<int,bool> f) { List<int> result=new List<int>(); foreach(int item in items) { if (f(item)) { result.Add(item); } } return result; } IEnumerable<int> ints = MyWhere(num, a => a > 100); foreach (var x in ints) { Console.WriteLine(x); } Console.ReadLine();
更牛的一种写法
Console.WriteLine("--------------更牛的写法yield-------------------"); //更牛的写法yield static IEnumerable<int> MyWhere2(IEnumerable<int> items, Func<int, bool> f) { foreach (int item in items) { if (f(item)) { yield return item;//优点,流水线处理,边执行边返回,效率更高 } } } IEnumerable<int> int2 = MyWhere(num, a => a > 100); foreach (var x in int2) { Console.WriteLine(x); }
补充知识
赋值后类型就确定了,不能再更改了,可以反编译看一下
后面会讲匿名类型+var才能真正发挥var的作用
P25:常用扩展方法一
IEnumerable<T>的扩展方法
实现了 IEnumerable接口的都可以使用LINQ扩展方法,如数组、List、Dictionary、Set.....
基础数据
public class Employee { public long Id { get; set; } public string Name { get; set; } public int Age { get; set; } public bool Gender { get; set; }//性别 public int Salary { get; set; } public override string ToString() { return $"Id={Id},Name={Name},Age={Age},Gender={Gender},Salary={Salary}"; } }
List<Employee> list=new List<Employee>(); list.Add(new Employee() { Id = 1, Name = "jerry", Age = 28, Gender = true, Salary = 5000 }); list.Add(new Employee() { Id = 2, Name = "jim", Age = 33, Gender = true, Salary = 3000 }); list.Add(new Employee() { Id = 3, Name = "lily", Age = 35, Gender = false, Salary = 9000 }); list.Add(new Employee() { Id = 4, Name = "lucy", Age = 16, Gender = false, Salary = 2000 }); list.Add(new Employee() { Id = 5, Name = "kimi", Age = 25, Gender = true, Salary = 1000 }); list.Add(new Employee() { Id = 6, Name = "nancy", Age = 35, Gender = false, Salary =85000 }); list.Add(new Employee() { Id = 7, Name = "zack", Age = 35, Gender = true, Salary = 8500 }); list.Add(new Employee() { Id = 8, Name = "djd", Age = 33, Gender = true, Salary = 8000 });
Where方法
IEnumerable<Employee> employees = list.Where(x => x.Age > 30); foreach(Employee employee in employees) { Console.WriteLine(employee); } Console.ReadLine();
注意区别List的count属性
Console.WriteLine(list.Count());//无参数 Console.WriteLine(list.Count(x=>x.Salary>5000));//有参数 Console.WriteLine(list.Count(x=>x.Salary>8000 && x.Age>20));//多条件参数 Console.ReadLine();
Console.WriteLine(list.Any(x => x.Age == 35));//至少有一条返回TRUE,可以用Count,但是Count要数完了才能返回,效率低 Console.WriteLine(list.Any(x => x.Age == 35000));
P26:常用扩展方法二
Single:必须取到有且只有一条,否则报错
SingleOrDefault:最多一条或者无返回数据,无返回数据时根据返回类型推断(可能是null或者int等等,看具体返回值类型),多条报错
First:取第一条,无数据报错
FirstOrDefault:取第一条,无数据返回默认值
//-----------------P26-------------------- Employee employee1; //employee1= list.Single(); //Console.WriteLine(employee1);//:“Sequence contains more than one element” //employee1 = list.Single(x=>x.Name== "djd");//和下一行一样 //employee1 = list.Where(x=>x.Name=="djd").Single(); employee1 = list.SingleOrDefault(x => x.Name == "djdd");//没有则返回null(根据实际情况返回) //employee1 = list.SingleOrDefault(x => x.Gender == true);//存在多条报错 Console.WriteLine(employee1); Employee employee2; employee2 = list.First(e=>e.Age>30); Console.WriteLine(employee2); employee2 = list.FirstOrDefault(e=>e.Age > 300);//返回null Console.WriteLine(employee2);
Order排序,对之前的数据不受影响,返回的新的数据集合排序
排序不一定非得要用属性
IEnumerable<Employee> item=list.OrderBy(x=>x.Age); foreach(Employee employee in item) { Console.WriteLine(employee); } int[] nums=new int[] {1,5,7,5,5,8,88,1,5,2,2,5,4,2,55}; IEnumerable<int> ints=nums.OrderBy(x=>x);//必须加一个参数,没有就加自己本身 foreach (int i in ints) { Console.WriteLine(i); } //var item2=list.OrderBy(x=>Guid.NewGuid());//随机排序方法一 Random rand=new Random();//随机排序方法二 var item2 = list.OrderBy(x => rand.Next()); foreach (Employee employee in item2) { Console.WriteLine(employee); } Console.WriteLine("----------------------"); var item3 = list.OrderBy(x => x.Name[x.Name.Length-1]);//取姓名最后一个字母排序 foreach (Employee employee in item3) { Console.WriteLine(employee); } Console.ReadLine();
一个OrderBy只能对一个字段排序,多个字段排序不能使用两个OrderBy,ThenBy可以连续多个使用
item3 = list.OrderBy(x => x.Age).ThenBy(x=>x.Salary).ThenByDescending(x=>x.Name);// foreach (Employee employee in item3) { Console.WriteLine(employee); }
var item4 =list.Where(e=>e.Age>30).OrderBy(e=>e.Name).Skip(2).Take(3);//链式操作,Skip\Take也可以单独使用 foreach (Employee employee in item4) { Console.WriteLine(employee); }
P27:常用扩展方法三
var itemMaxAge = list.Max(e => e.Age);//年龄最大值 Console.WriteLine(itemMaxAge); var itemMaxSalary = list.Where(e=>e.Id>6).Max(e => e.Salary);//序号大于6的工资最大值 Console.WriteLine(itemMaxSalary); var itemMaxName = list.Max(e => e.Name);//同样可以返回字符串的最大值 Console.WriteLine(itemMaxName); var itemAvgSalary = list.Where(e => e.Age > 30).Average(e => e.Salary);//年龄大于30岁的平均工资,返回Double类型 Console.WriteLine(itemAvgSalary);
//IEnumerable<IGrouping<int, Employee>> groupings = list.GroupBy(e => e.Age).OrderBy(g => g.Key);//完整写法//IGrouping继承IEnumerable,所以可以先理解为一样的处理方式 var itemgroup =list.GroupBy(e => e.Age).OrderBy(g=>g.Key); foreach (var group in itemgroup) { Console.WriteLine($"------------{group.Key}--------------"); Console.WriteLine($"最大工资:{group.Max(e=>e.Salary)}"); foreach(Employee emp in group) { Console.WriteLine(emp); } Console.WriteLine("----------------------------"); }
运行结果
补充一个多字段分组的代码写法案例
Console.WriteLine("---------------多字段分组----------------"); var itemgroupMult = list.GroupBy(e => new { e.Age, XB = e.Gender ? "男" : "女" }).OrderBy(g => g.Key.Age).ThenBy(g => g.Key.XB);//.OrderBy(g => g.Key);这样写语法不报错,运行报错 foreach (var group in itemgroupMult) { Console.WriteLine($"------------{group.Key}--------------"); Console.WriteLine($"最大工资:{group.Max(e => e.Salary)}"); foreach (Employee emp in group) { Console.WriteLine(emp); } Console.WriteLine("----------------------------"); }
P28:常用扩展方法四
Select就是投影操作
var item281 = list.Select(e => e.Age);//返回IEnumerable<int>集合 foreach(var emp in item281) { Console.WriteLine(emp); } var item282 = list.Where(e=>e.Age>30).Select(e => e.Name+","+e.Age + "," + (e.Gender?"男":"女"));//返回IEnumerable<string>集合 foreach (var emp in item282) { Console.WriteLine(emp); } Console.WriteLine("---------------投影为Dog类型----------------"); var item283=list.Select(e => new Dog { NickName=e.Name,Age= e.Age }); foreach (var emp in item283) { Console.WriteLine(emp.NickName+","+emp.Age); }
//匿名类型,只能使用var定义 //反编译后会发现,编译器随机起了一个类名 var obj1 = new { Name = "ddd", Age = 28, Salary = 20000 }; Console.WriteLine(obj1.Name + "," + obj1.Age + "," + obj1.Salary + ",");
Console.WriteLine("---------------投影为匿名类型----------------"); var item284 =list.Select(list => new { NickName=list.Name,Age= list.Age,XingBie=list.Gender?"男":"女" });//这里只能使用var获取返回值,由编译器推断 foreach(var emp in item284)//var推断 { Console.WriteLine(emp.NickName + "," + emp.Age + "," + emp.XingBie + ","); } Console.WriteLine("---------------投影为匿名类型复杂应用----------------"); var item285 = list.GroupBy(e => e.Age).Select(g => new { g.Key,AvgSalart= g.Average(e => e.Salary),MaxSalary=g.Max(e=>e.Salary) }); foreach (var emp in item285) { Console.WriteLine(emp.Key + "," + emp.AvgSalart + "," + emp.MaxSalary + ","); }
P29:链式调用
IEnumerable<Employee> item291 = list.Where(e => e.Salary > 5000); List<Employee> employees1 = item291.ToList();//转为LIst类型 Employee[] employees2 = employees1.ToArray();//转为数组类型
Console.WriteLine("---------------链式调用小考----------------"); var item292 =list.Where(e=>e.Id>2).GroupBy(e=>e.Age)//使用GroupBy以后的链接操作都是针对Group的 .OrderBy(g=>g.Key) .Take(3) .Select(g=>new {NL=g.Key,RS=g.Count(),PJGZ=g.Average(e=>e.Salary)});//注意g、e各自代表的数据范围 foreach(var emp in item292) { Console.WriteLine(emp.NL + "," + emp.RS + "," + emp.PJGZ); }
P30:LINQ另一种倩影
可以不用管,只是写法不一样罢了,看到能明白就行
写LINQ更像是写SQL语句
Console.WriteLine("---------------类似于SQL的一种写法----------------"); Console.WriteLine("---------------编译后代码一样,看懂就行----------------"); var item293 = from e in list where e.Salary > 500 select new { e.Age, e.Name, XB = e.Gender ? "男" : "女" }; foreach (var emp in item293) { Console.WriteLine(emp.Age + "," + emp.Name + "," + emp.XB); }
P31:LINQ解决面试问题
三个数取最大值
Console.WriteLine("---------------三个数取最大值----------------"); int ii = 5; int ij = 6; int ik= 7; int[] ints1= new int[] { ii,ij,ik}; Console.WriteLine(ints1.Max(e => e));//LINQ方法 Console.WriteLine(Math.Max(ii, Math.Max(ij, ik)));//基础方法
Console.WriteLine("---------------案例一----------------"); string s = "61,90,100,99,22,3,5,333,2,3,3,2,3"; string[] ints2 = s.Split(","); var p311= ints2.Select(e=>int.Parse(e)); Console.WriteLine(p311.Average(e=>e));//分布实现 Console.WriteLine(s.Split(",").Select(e => int.Parse(e)).Average(e=>e));//一步实现
Console.WriteLine("---------------案例二----------------"); string s2 = "Hello,world,HHLIk;";//string实现了IEnumerable<char> //去掉Where筛选发现排序失败了 //var x311 = s2.Where(c => char.IsLetter(c)) // .Select(c => char.ToLower(c)) // .GroupBy(e => e) // .OrderByDescending(g => g.Key) // .Where(g => g.Count() > 2) // .Select(g => new { g.Key, PL = g.Count() }); //foreach (var x in x311) //{ // Console.WriteLine(x); //} Console.WriteLine("-------------------------------"); var x311 = s2.Where(c => char.IsLetter(c)) .Select(c => char.ToLower(c)) .GroupBy(e => e) .Select(g => new { g.Key, PL = g.Count() }) .OrderByDescending(g => g.PL) .Where(g => g.PL > 2); foreach (var x in x311) { Console.WriteLine(x); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix