C# 基础知识系列- 7 Linq详解(一)

运维

  前言

  在上一篇中简单介绍了Linq的入门级用法,这一篇尝试讲解一些更加深入的使用方法,与前一篇的结构不一样的地方是,这一篇我会先介绍Linq里的方法,然后以实际需求为引导,分别以方法链的形式和类SQL的形式写出来。

  前置概念介绍

  Predicate<T> 谓词、断言,等价于 Func<T,bool> 即返回bool的表达式Expression<TDelegate> 表达式树,这个类很关键,但是在这里会细说,我们会讲它的一个特殊的泛型类型:Expression<Func<T,bool>> 这个在某些数据源的查询中十分重要,它代表lambda表达式中一种特殊的表达式,即没有大括号和return关键字的那种。我们先准备两个类:

  Student/学生类:/// <summary>

  /// 学生

  /// </summary>

  public class Student

  {

   /// <summary>

   /// 学号

   /// </summary>

   public int StudentId { get; set; }

   /// <summary>

   /// 姓名

   /// </summary>

   public string Name { get; set; }

   /// <summary>

   /// 班级

   /// </summary>

   public string Class { get; set; }

   /// <summary>

   /// 年龄

   /// </summary>

   public int Age { get; set; }

  }Subject/科目类:/// <summary>

  /// 科目

  /// </summary>

  public class Subject

  {

   /// <summary>

   /// 名称

   /// </summary>

   public string Name { get; set; }

   /// <summary>

   /// 年级

   /// </summary>

   public string Grade { get; set; }

   /// <summary>

   /// 学号

   /// </summary>

   public int StudentId { get; set; }

   /// <summary>

   /// 成绩

   /// </summary>

   public int Score { get; set; }

  }

  Subject 和Student通过学号字段一一关联,实际工作中数据表有可能会设计成这。

  那么先虚拟两个数据源:IEnumerable<Student> students 和 IEnumerable<Subject> subjects。先忽略这两个数据源的实际来源,因为在开发过程中数据来源有很多种情况,有数据库查询出来的结果、远程接口返回的结果、文件读取的结果等等。不过最后都会整理成IEnumerable<T>的子接口或实现类的对象。

  常见方法介绍

  Where 过滤数据,查询出符合条件的结果

  where的方法声明:

  public IEnumerable<TSource> Where<TSource> (this IEnumerable<TSource> source, Func<TSource,bool> predicate)可以看出不会转换数据类型,通过给定的lambda表达式或者一个方法进行过滤,获取返回true的元素。

  示例:

  // 获取年纪大于10但不大于12的同学们List<Student> results = students.Where(t=>t.Age >10 && t.Age<= 12).ToList();注意在调用ToList之后数据才会实质上查询出来。

  Group 分组,依照指定内容进行分组

  Group的方法声明有很多种:

  最常用的一种是:

  public static IEnumerable<System.Linq.IGrouping<TKey,TSource>> GroupBy<TSource,TKey> (this IEnumerable<TSource> source, Func<TSource,TKey> keySelector);示例:

  //将学生按照班级进行分组List<IGrouping<string,Student>> list = students.GroupBy(p => p.Class).ToList();

  OrderBy/OrderByDescing 进行排序,按条件升序/降序

  它们是一对方法,一个是升序一个降序,其声明是一样的:

  常用的是:

  public static System.Linq.IOrderedEnumerable<TSource> OrderBy<TSource,TKey> (this IEnumerable<TSource> source, Func<TSource,TKey> keySelector);示例:

  //按年龄的升序排列:List<Student> results = students.OrderBy(p => p.Age).ToList();//按年龄的降序排列:List<Student> results = students.OrderByDescing(p => p.Age).ToList();

  First/Last 获取数据源的第一个/最后一个

  这组方法有两个常用的重载声明:

  First:

  // 直接获取第一个public static TSource First<TSource> (this IEnumerable<TSource> source);// 获取满足条件的第一个public static TSource First<TSource> (this IEnumerable<TSource> source, Func<TSource,bool> predicate);Last:

  // 直接获取最后一个public static TSource Last<TSource> (this IEnumerable<TSource> source);// 获取最后一个满足条件的元素public static TSource Last<TSource> (this IEnumerable<TSource> source, Func<TSource,bool> predicate);

  示例:

  Student student = students.First();// 等价于 students[0];Student student = students.First(p=>p.Class == "一班");//获取数据源中第一个一班的同学​Student student = students.Last();//最后一个学生Student student = students.Last(p=>p.Class == "三班");//获取数据源中最后一个三班的同学

  注意:

  在某些数据源中使用Last会报错,因为对于一些管道类型的数据源或者说异步数据源,程序无法确认最后一个元素的位置,所以会报错。解决方案:先使用OrderBy对数据源进行一次排序,使结果与原有顺序相反,然后使用First获取当数据源为空,或者不存在满足条件的元素时,调用这组方法会报错。解决方案:调用FirstOrDefault/LastOrDefault,这两组方法在无法查询到结果时会返回一个默认值。

  Any/All 是否存在/是否都满足

  Any:是否存在元素满足条件

  有两个版本,不过意思可能不太一样:

  public static bool Any<TSource> (this IEnumerable<TSource> source);//数据源中是否有数据//================//是否存在满足条件的数据public static bool Any<TSource> (this IEnumerable<TSource> source, Func<TSource,bool> predicate);

  All :是否都满足条件:

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

  示例:

  // 是否有学生bool isAny = students.Any();// 是否有五班的同学bool isFive = students.Any(p=>p.Class == "五班");// 是否所有学生的年纪都不小于9岁bool isAll = students.All(p=>p.Age >= 9);

  Skip 略过几个元素

  Skip一共有三个衍生方法:

  第一个:Skip 自己: 略过几个元素,返回剩下的元素内容

  public static IEnumerable<TSource> Skip<TSource> (this IEnumerable<TSource> source, int count);第二个:SkipLast,从尾巴开始略过几个元素,返回剩下的元素内容

  public static IEnumerable<TSource> SkipLast<TSource> (this IEnumerable<TSource> source, int count);第三个:SkipWhile,跳过满足条件的元素,返回剩下的元素

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

  示例:

  // 不保留前10个学生List<Student> results = students.Skip(10).ToList();// 不保留后10个学生List<Student> results = students.SkipLast(10).ToList();// 只要非一班的学生List<Student> results = students.SkipWhere(p=>p.Class=="一班").ToList();//上一行代码 等价于 = students.Where(p=>p.Class != "一班").ToList();

  Take 选取几个元素

  Take与Skip一样也有三个衍生方法,声明的参数类型也一样,这里就不对声明做介绍了,直接上示例。

  //选取前10名同学List<Student> results = students.Take(10).ToList();// 选取最后10名同学List<Student> results = students.TakeLast(10).ToList();//选取 一班的学生List<Student> results = students.TakeWhile(p=>p.Class=="一班").ToList();// 上一行 等价于 = students.Where(p=>p.Class=="一班").ToList();

  在使用Linq写分页的时候,就是联合使用Take和Skip这两个方法:

  int pageSize = 10;//每页10条数据int pageIndex = 1;//当前第一页List<Student> results = students.Skip((pageIndex-1)*pageSize).Take(pageSize).ToList();其中 pageIndex可以是任意大于0 的数字。Take和Skip比较有意思的地方就是,如果传入的数字比数据源的数据量大,根本不会爆粗,只会返回一个空数据源列表。

  Select 选取

  官方对于Select的解释是,将序列中的每个元素投影到新的表单里。我的理解就是,自己 定义一个数据源单个对象的转换器,然后按照自己的方式对数据进行处理,选择出一部分字段,转换一部分字段。

  所以按我的理解,我没找到java8的同效果方法。(实际上java用的是map,所以没找到,:-D)

  public static System.Collections.Generic.IEnumerable<TResult> Select<TSource,TResult> (this IEnumerable<TSource> source, Func<TSource,TResult> selector);

  示例:

  // 选出班级和姓名List<object> results = students.Select(p => new{ p.Class, p.Name}).ToList();

  以上是今天的内容,Linq篇的内容依旧未完,下篇继续讲解。

标签: 运维