翼度科技»论坛 编程开发 .net 查看内容

LINQ 学习之路

4

主题

4

帖子

12

积分

新手上路

Rank: 1

积分
12
一、为什么要使用 LINQ

要理解为什么使用 LINQ,先来看下下面的例子
例子:要统计字符串中每个字母出现的频率(忽略大小写),然后按照从高到低的顺序输出出现频率高于2次和其出现的的频率。如果用传统的 Sql 语句来写,一定是非常的繁杂,如果用 LINQ 语句来写,效果如下
  1. string strs = "hello word, Hehehe";
  2. var items = strs.Where(c => char.IsLetter(c)) //过滤非字符
  3.             .Select(c => char.IsLower(c))  //大小写都转换成小写
  4.             .GroupBy(c => c) //根据字母进行分组
  5.             .Where(g => g.Count() > 2) //过滤掉出现次数 <=2
  6.             .OrderByDescending(g => g.Count()) //按次数排序
  7.             .Select(g => new { Char = g.Key, Count = g.Count() });
复制代码
使用 Linq 来实现,简单、清晰、明了。这里就体现了 LINQ 的一大好处:让数据处理变得简单
二、揭秘 LINQ 方法的背后

了解 LINQ 方法背后做了些什么,可以让我们更好的使用 LINQ。下面以 Wherr 方法为例,写一个我们自己的 MyWhere 方法
LINQ 提供了很多集合的扩展方法,配合 lambda 能简化数据处理
例子:有一个 [3, 15, 88, 4, 77, 42, 8] 的数组,要得到它 >10 的值
  1. int[] num = new int[] { 3, 15, 88, 4, 77, 42, 8 };
  2. //使用原本的Where
  3. IEnumerable<int> result = num.Where(r => r > 10);
  4. foreach (int i in result)
  5. {
  6.     Console.WriteLine(i);
  7. });
  8. }
复制代码
  1. static void Main(string[] args)
  2. {
  3.     int[] num = new int[] { 3, 15, 88, 4, 77, 42, 8 };
  4.     //使用我们自己写的MyWhere方法
  5.     var result2 = MyWhere2(num, r => r > 10);
  6.     foreach (int i in result2)
  7.     {
  8.         Console.WriteLine(i);
  9.     }           
  10. }
  11. public static IEnumerable<int> MyWhere(IEnumerable<int> items, Func<int, bool> f)
  12. {
  13.     List<int> result = new List<int>();
  14.     foreach (var i in items)
  15.     {
  16.         if (f(i) == true)
  17.         {
  18.             result.Add(i);
  19.         }
  20.     }
  21.     return result;
  22. }
复制代码
在上述代码中,MyWhere 方法是根据我们的需求来写的一个扩展方法,它要求传入一个IEnumerable 类型的参数和一个 Func 委托类型的参数,在方法中,新建一个 List 集合用于存值,遍历传入的数组,符合条件就存入集合中,最后返回集合。
三、LINQ 的扩展方法

LINQ 关键的功能是提供了集合类的扩展方法,所以实现了 IEnumerable 接口的类都可以使用这些方法,这是方法并不是 IEnumerable 中的方法,而是以扩展方法的存在于System.Linq 命名空间的静态类中
准备一些测试数据
  1. class Employee
  2. {
  3.     public long Id { get; set; }
  4.     public string Name { get; set; }
  5.     public int Age { get; set; }
  6.     public bool Gender { get; set; }
  7.     public int Salary { get; set; }
  8.     public override string ToString()
  9.     {
  10.         return $"Id={Id},Name={Name},Age={Age},Gender={Gender},Salary={Salary}";
  11.     }
  12. }
复制代码
Where(数据过滤)

Where 方法是根据条件对数据进行过滤
语法如下:
  1. IEnumerable<Employee> items = list.Where(e => e.Age > 25);
  2. foreach (var item in items)
  3. {
  4.     Console.WriteLine(item);
  5. }
复制代码
Count(获取数据条数)

语法如下:
  1. //返回一个数字,表示指定序列中满足条件的元素个数。
  2. Console.WriteLine(list.Count(e => e.Salary > 5000 || e.Age > 25));
复制代码
Any(判断是否有一条数据满足条件)

语法如下:
  1. //是否至少有一条数据
  2. //性能比 Count 要高,Count 会遍历所有数据才返回结果,Any 在遍历数据时遇到满足条件的数据就直接返回结果
  3. Console.WriteLine(list.Any(e => e.Salary == 8000));
  4. Console.WriteLine(list.Where(e=>e.Salary>8000).Any());
复制代码
Single、SingleOrDefault、First、FirstOrDefault(获取一条数据)

语法如下:
  1. //有且只有一条数据,如果没有符合条件的数据或者存在大于一条符合条件的数据,都会报错
  2. Console.WriteLine(list.Single(s => s.Name == "张德开"));
  3. //有且只有一条数据,返回该条数据,否则返回默认值,如果匹配到多条数据,则会报错
  4. Console.WriteLine(list.SingleOrDefault(s => s.Age == 18));
  5. //匹配到多条数据时,返回第一条数据,没有匹配到数据时,则会报错
  6. Console.WriteLine(list.First(e => e.Age > 30));
  7. //匹配到多条数据时,返回第一条数据,否则返回默认值
  8. Console.WriteLine(list.FirstOrDefault(e => e.Age > 30));
复制代码
OrderBy、OrderByDescending(排序)

语法如下:
  1. //升序排序
  2. IEnumerable<Employee> e = list.OrderBy(s => s.Age);
  3. //降序排序
  4. e = list.OrderByDescending(s => s.Salary);
  5. e = list.OrderByDescending(s => s.Name[s.Name.Length - 1]);
  6. //可以在OrderBy、OrderByDescending后面继续使用ThenBy、ThenByDescending进行多规则排序
  7. e = list.OrderBy(s => s.Age).ThenBy(s => s.Salary);
  8. foreach (var item in e)
  9. {
  10.      Console.WriteLine(item);
  11. }
复制代码
Sike()、Take()(限制结果集)

语法如下:
  1. // Skip(n) 跳过 n 条数据,Take(n) 获取 n 条数据
  2. var items2 = list.Skip(3).Take(2);
  3. items2 = list.Where(e => e.Age >= 25).OrderBy(e => e.Age).Skip(2).Take(3);
  4. foreach (var item in items2)
  5. {
  6. Console.WriteLine(item);
  7. }
复制代码
聚合函数

Max(最大值)、Min(最小值)、Average(平均值)、Sun(总和)、Count(总数)
语法如下:
  1. //最大年龄
  2. Console.WriteLine(list.Max(e => e.Age));
  3. //最低工资
  4. Console.WriteLine(list.Min(e => e.Salary));
  5. //平均工资
  6. Console.WriteLine(list.Average(e => e.Salary));
  7. //所有人总工资
  8. Console.WriteLine(list.Sum(e => e.Salary));
  9. //女员工数量
  10. Console.WriteLine(list.Count(e => e.Gender == false));
复制代码
GroupBy(分组)

GroupBy 方法的参数 keySelector 是分组条件表达式,GroupBy 方法的返回值为IGrouping 类型的泛型 IEnumerable。IGrouping 是继承自 IEnumerable的接口,IGrouping 中唯一的成员就是 Key 属性,表示这一组数据的数据项,由于IGrouping 是继承自 IEnumerable 接口的,因此我们依然可以使用 Count、Min、Average等方法对组内数据进行聚合运算
语法如下:
  1. //根据年龄进行分组
  2. IEnumerable<IGrouping<int, Employee>> items3 = list.GroupBy(e => e.Age);
  3. foreach (IGrouping<int, Employee> g in items3)
  4. {
  5.      Console.WriteLine(g.Key);
  6.      foreach (Employee e in g)
  7.      {
  8.          Console.WriteLine(e);
  9.      }
  10. }
复制代码
综合案例:
  1. //根据年龄分组,获取每组人数,最大工资,平均工资,用var简化编程
  2. var item3 = list.GroupBy(e => e.Age);
  3. foreach (var g in item3)
  4. {
  5.     Console.WriteLine("每组人数:" + g.Count());
  6.     Console.WriteLine("最大工资:" + g.Max(e => e.Salary));
  7.     Console.WriteLine("平均工资:" + g.Average(e => e.Salary));
  8.     foreach (var e in g)
  9.     {
  10.         Console.WriteLine(e);
  11.     }
  12. }
复制代码
Select(投影)

可以对集合使用 Select 方法进行投影操作,通俗来讲就是把集合中的每一项逐渐转换为另外一种类型,Select 方法的参数是转换的表达式。
语法如下:
  1. //把集合中的每一项转换成另一个类型
  2. var items4 = list.Select(e => e.Age);
  3. //Select 方法把 bool 的 Gender 转换为字符串类型,Select 方法的返回值为 IEnumerable<string> 类型
  4. var items4 = list.Where(e => e.Salary > 5000).Select(e => e.Gender ? "男" : "女");
  5. //根据年龄分组,然后统计各组人数、年龄、最高工资、最低工资
  6. var items4 = list.GroupBy(e => e.Age).Select(s => new { NianLing = s.Key, MaxS = s.Max(e => e.Salary), MinS = s.Min(e => e.Salary), RenShu = s.Count() });
  7. foreach (var e in items4)
  8. {
  9.     Console.WriteLine(e.NianLing + ',' + e.MaxS + "," + e.MinS + ',' + e.RenShu);
  10. }
复制代码
集合转换

语法如下:
  1. // ToList() 将其他类型转换为 List<T> 类型
  2. List<Employee> empList = list.Where(e => e.Age > 25).ToList();
  3. // ToArray() 将其他类型转换为数组
  4. Employee[] empArr = list.Where(e => e.Salary > 5000).ToArray();
复制代码
链式调用

上述介绍的这些方法,Where、Count、OrderBy、GroupBy 等方法的返回值都是 IEnumerable 类型,因此它们是可以链式调用的
  1. //获取 id>2 的数据,然后按照 Age 分组,并且把分组按照 Age 排序,然后取出前三条,最后再投影取得年龄、人数、平均工资
  2. var items5 = list.Where(e => e.Id > 2).GroupBy(g => g.Age).OrderBy(g => g.Key).Take(3)
  3.                 .Select(e => new { LianLin = e.Key, renShu = e.Count(), pingJun = e.Average(e => e.Salary) });
  4. foreach (var i in items5)
  5. {
  6.     Console.WriteLine(i.pingJun + "," + i.renShu + "," + i.pingJun);
  7. }
复制代码
四、LINQ 的另一种写法

LINQ 有两种语法,分别为方法语法和查询语法
  1. //方法语法:使用Where、OrderBy、Selsect等扩展方法来查询数据的写法叫做Linq方法语法
  2. var items6 = list.Where(e => e.Salary > 3000).OrderBy(e => e.Age)
  3.                 .Select(e => new { e.Name, e.Age, XB = e.Gender ? "男" : "女" });
复制代码
  1. //查询语法:使用 LINQ 声明性查询语法编写的查询语句,在编译代码时,查询语法必须转换为针对.Net公共语言的的调用,这些方法的调用会调用标准查询运算符
  2. var items7 = from e in list
  3.              where e.Salary > 3000
  4.              orderby e.Age
  5.              select new
  6.              {
  7.                  e.Name,
  8.                  e.Age,
  9.                  XB = e.Gender ? "男" : "女"
  10.              };
复制代码
方法语法

“方法语法” 并没有发明新的语法,用的都是扩展方法、Lambda 表达式等 C# 中已经存在的语法,而 “查询语法” 则是新的 C# 语法 。编译器会把 “查询语法” 编译成 “方法语法” 形式,因此它们在运行时没有区别。所有的 “查询语法” 都能改写成 “方法语法”,所有的 “方法语法”也能改写成 “查询语法” 。
查询语法

“查询语法” 看起来更加新颖,而且比 “方法语法” 需要写的代码会少一点,但在编写复杂的查询条件的时候,用 “方法语法” 编写的代码会更清晰
五、小练习

练习一

"85,34,23,45,12,67,60" 计算这些数的平均值
  1. string s = "85,34,23,45,12,67,60";
  2. var avg = s.Split(',').Select(e => Convert.ToInt32(e)).Average();
  3. Console.WriteLine(avg);
复制代码
练习二

统计一个字符串中每个字母出现的频率(忽略大小写),然后按照从高到低的顺序输出出现频率高于2次的单词和出现的频率
  1. string s = "Hello word Hehehe Hahaha";
  2. var items7 = s.Where(e => char.IsLetter(e)).Select(e => char.ToLower(e)).GroupBy(c => c)
  3.     .Select(g => new { g.Key, Count = g.Count() }).OrderBy(g => g.Count).Where(g => g.Count > 2);
  4. foreach (var item in items7)
  5. {
  6.     Console.WriteLine(item);
  7. }
复制代码
来源:https://www.cnblogs.com/baiye123/archive/2023/08/22/17647997.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具