c# Linq常用的小技巧

前言

在C#语言发展的历史长河中,Linq是一个极其重要的里程碑!

Linq的语法吸取了SQL语法的特性,同时配合Lambda表达式又可以使代码更加优雅!
可以这么说,用好了Linq可以大大提高程序猿的工作效率,毕竟我们的日常工作本质就是对数据的处理。经历了十多年的发展,现在微软自带的内库包含的Linq函数已经非常多了,几乎满足我们日常工作。

下面根据一个对科室数据操作的例子,就个人觉得日常高频使用的Linq小技巧贴出来,权当是做个笔记了。

初始化数据

定义模型

这里定义一个科室对象,模拟我们日常工作的科室信息。科室存在层级关系,还有一个员工数量的属性。模型如下:

 public class DepartmentDto
 {
  public int Id { get; set; }   

  public int? ParentId { get; set; }

  public string Name { get; set; }

  public string TelPhone { get; set; }

  public string Address { get; set; }

  public string Remark { get; set; }

  public int EmployeeNumber { get; set; }
 }

初始化数据

  public List<DepartmentDto> InitDepartmentData()
  {
   List<DepartmentDto> lst = new List<DepartmentDto>();
   lst.AddRange(new DepartmentDto[] {
    new DepartmentDto() {
      Address ="一马路XX号",
      Id=1,
      Name="一级一号科室",
      Remark="",
      TelPhone="0731-6111111",
      EmployeeNumber=3,
    },
    new DepartmentDto() {
      Address ="二马路XX号",
      Id=2,
      Name="一级二号科室",
      Remark="",
      TelPhone="0731-6111111",
      EmployeeNumber=4,
    },
    new DepartmentDto() {
      Address ="三马路XX号",
      Id=3,
      Name="一级三号科室",
      Remark="",
      TelPhone="0731-6222222",
      EmployeeNumber=6,
    },
    new DepartmentDto() {
      Address ="一马路XX号",
      ParentId=1,
      Id=4,
      Name="二级一号科室",
      Remark="",
      TelPhone="0731-6222222",
      EmployeeNumber=7,
    },
    new DepartmentDto() {
      Address ="二马路XX号",
      ParentId=2,
      Id=5,
      Name="二级二号科室",
      Remark="",
      TelPhone="0731-6222222",
      EmployeeNumber=5,
    },
   });
   return lst;
  }

获取未存在父级科室的科室集合

 List<DepartmentDto> lstDepartItems = InitDepartmentData();
   //1、获取未存在父级科室的科室集合 1、2、3
   List<DepartmentDto> notExistsParentDepartmentIdLst = lstDepartItems
    .Where(p => !p.ParentId.HasValue)
    .ToList();

这里比较简单,Where内校验下ParentId的值为空即可,不使用Linq则需要自己手写一个循环搞定,如下:

 List<DepartmentDto> notExistsParentDepartmentIdLst_1 = new List<DepartmentDto>();
 foreach (DepartmentDto department in lstDepartItems)
 {
   if (!department.ParentId.HasValue)
    notExistsParentDepartmentIdLst_1.Add(department);
 }

这么看感觉便捷性不太明显是吧~~ 没事,万丈高楼平地起,咋们循行渐进~

获取存在子科室的科室集合

 //2、获取存在子科室的科室集合 1、2
   List<DepartmentDto> existsParentDepartmentIdLst1 = lstDepartItems
    .Where(p => lstDepartItems.Select(k => k.ParentId).Contains(p.Id))
    .ToList();

这里通过引用了外部的集合对象进行关联,在Where内对子科室的ParentId字段与当前集合的科室Id校验,从而得到已存在子科室的科室集合。如果不使用Linq则自己需要写两个循环才能搞定。
如下:

 List<DepartmentDto> existsParentDepartmentIdLst1_1 = new List<DepartmentDto>();
 foreach (DepartmentDto parentDepart in lstDepartItems)
 {
  foreach (DepartmentDto childDepart in lstDepartItems)
  {
   if (parentDepart.Id == childDepart.ParentId)
   {
     existsParentDepartmentIdLst1_1.Add(parentDepart);
     continue;
   }
  }
 }

这么看,Linq带来的便捷性是否足够明显了,代码优雅了太多了~

科室根据地址分组,获取科室总数、科室平均数

 var groupDto = lstDepartItems
    .GroupBy(p => p.Address)
    .Select(p => new
    {
     Address = p.Key,
     SumEmployeeCount = p.Sum(p => p.EmployeeNumber),
     AvgEmployeeCount = p.Average(p => p.EmployeeNumber),
    }).ToList();

获取两个集合不相等的元素

引用类型的比较处理

这里需要留意我们的科室对象是class,class在C#里属于引用类型。引用类型的比较和值类型的比较大不同相同!引用类型的比较是比较对象在内存堆里指向的地址,并非对象包含的属性值 但是我们预期的比较就是单纯的对值进行比较,所以这里需要通过实现IEqualityComparer<T>接口来对两个引用类型集合对象进行比较。

创建IEqualityComparer<DepartmentDto>对象

 public class DepartmentEqualityComparer : IEqualityComparer<DepartmentDto>
 {
  public bool Equals([AllowNull] DepartmentDto x, [AllowNull] DepartmentDto y)
  {
   // 这里可以写比较的关键代码~
   return x.Id == y.Id;
  }

  public int GetHashCode([DisallowNull] DepartmentDto obj)
  {
   return obj.ToString().GetHashCode();
  }
 }

接下来,定义一个新的集合,再手动向这个集合追加元素。

 List<DepartmentDto> lstDepartItemsCPs = InitDepartmentData();
   lstDepartItemsCPs.Add(new DepartmentDto()
   {
    Address = "三马路XX号",
    Id = 6,
    Name = "二级三号科室",
    Remark = "",
    TelPhone = "0731-6222222",
    EmployeeNumber = 7
   });

集合比较代码:

// 这里如果DepartmentDto为引用类型(class)则需要使用比较器DepartmentEqualityComparer才能返回我们的预期值(根据ID值判断是否相等)
   List<DepartmentDto> diffList = lstDepartItemsCPs.Except(lstDepartItems, new DepartmentEqualityComparer()).ToList();
   // 获取相等元素
   List<DepartmentDto> diffList1 = lstDepartItemsCPs.Intersect(lstDepartItems.Select(p => p), new DepartmentEqualityComparer()).ToList();
  // 需要添加IEqualityComparer,因为集合内的内容为引用类型!所以两个集合的“值”是不同的,引用类型的值在这里还包含了指向的内存堆的引用地址
   bool isEqual = lstDepartItems.SequenceEqual(InitDepartmentData(), new DepartmentEqualityComparer());

值类型的比较处理

可能你觉得需要去创建IEqualityComparer<DepartmentDto>对象过于麻烦,那么想下是否一定需要将科室对象的类型设定为class,是否有更合适的类型可以替换? 答案是有的,微软推荐如果对具体模型不需要多次执行装箱、拆箱操作最好将模型设置为结构struct而非class。 现在我们回过头将科室对象的类型更新为struct

然后发现上面集合比较的代码可以简化成这样:

 // 这里如果DepartmentDto为值类型(struct)则不需要使用比较器DepartmentEqualityComparer即可返回我们的预期值(根据ID值判断是否相等)
   List<DepartmentDto> diffList3 = lstDepartItemsCPs.Except(lstDepartItems).ToList();
   // 获取相等元素
   List<DepartmentDto> diffList4 = lstDepartItemsCPs.Intersect(lstDepartItems.Select(p => p)).ToList();
     // 如果把DepartmentDto的类型改为值类型则可以不需要IEqualityComparer进行判断的结果也会为true
   isEqual = lstDepartItems.SequenceEqual(InitDepartmentData());

OfType和Cast的不同之处

OfType允许对集合的项进行隐性转换(非强转Convert)且在转换前会进行判断,当类型不允许转换则continue到下一个集合项。而Cast则是子项不进行判断,直接隐性转换,所以理论上效率更高,当然相对的出错率也更高~

  public void ConvertListTest()
  {
   try
   {
    object[] ss = { 1, "2", 3, "四", "五", "7" };
    // 1、3 OfType的本质是循环集合,对每个集合项进行类型验证(不是强转,所以此处的结果是1、3 而不是1、2、3、7)
    var lst = ss.ToList().OfType<int>().ToList();
    // 3
    int max = ss.OfType<int>().Max();
    // 这句代码会提示“System.InvalidCastException:“Unable to cast object of type 'System.String' to type 'System.Int32'.”异常,原因:Cast的执行效率会略高与OfType,因为在对集合项进行类型转换前不会对其进行类型校验,当你确保集合的类型是安全的则可以用Cast,但是能用到Cast和OfType的时候基本上都是用OfType了..
    int maxCast = ss.Cast<int>().Max();
   }
   catch (Exception ex)
   {
    Console.WriteLine(ex);
   }
  }

上述代码已上传到github,地址: https://github.com/QQ897878763/LinqStudySample.git

以上就是c# Linq常用的小技巧的详细内容,更多关于c# Linq小技巧的资料请关注我们其它相关文章!

(0)

相关推荐

  • c#使用linq把多列的List转化为只有指定列的List

    使用linq把多列的List转化为只有指定列的List 1.方式一 2.方式二 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们.

  • C# PLINQ 内存列表查询优化历程

    产品中(基于ASP.NET MVC开发)需要经常对药品名称及名称拼音码进行下拉匹配及结果查询.为了加快查询的速度,所以我最开始就将其加入内存中(大约有六万五千条数据). 下面附实体类. public class drugInfo { public int drug_nameid { get; set; } public string drug_name { get; set; } public string drug_search_code { get; set; } } 第一次做法: Stop

  • C#基于Linq和反射实现数据持久化框架Xml4DB详解

    我们知道目前大部分的数据库都是关系型数据库, 所谓关系型数据库,就是指建立在关系模型 基础之上的数据库系统,如Oracle.SQL Server.Access.MySQL等.关系模型就是指二维表格模型,因而一个关系型数据库就是由二维表及其之间的联系组成的一个数据组织.一个偶然的机会我接触到了DB4O,它是一个完全面向对象的开源数据库,它的出现完全颠覆了传统的数据库在人们心中的形象,因为传统的数据库需要在数据体.实体之间转换,而且需要映射文件提供映射关系.正是这个项目让我产生了编写Xml4DB的想

  • 详解c# PLINQ中的分区

    最近因为比较忙,好久没有写博客了,这篇主要给大家分享一下PLINQ中的分区.上一篇介绍了并行编程,这边详细介绍一下并行编程中的分区和自定义分区. 先做个假设,假设我们有一个200Mb的文本文件需要读取,怎么样才能做到最优的速度呢?对,很显然就是拆分,把文本文件拆分成很多个小文件,充分利用我们计算机中的多核cpu的优势,让每个cpu都充分的利用,达到效率的最大化.然而在PLINQ中也是,我们有一个数据源,如果想进行最大的并行化操作,那么就需要把其拆分为可以多个线程同时访问的多个部分,这就是PLIN

  • c#中LINQ的基本用法实例

    一.什么是LINQ LINQ(读音link)代表语言集成查询(Language Integrated Query),是.NEt框架的扩展,它允许我们用SQL查询数据库的方式来查询数据的集合,使用它,你可以从数据库.程序对象的集合以及XML文档中查询数据 下面一个简单的示例,可以查询数组中小于8的数字并输出. 一般步骤:获取数据源.创建查询.执行查询.需要注意的是,尽管查询在语句中定义,但直到最后的foreach语句请求其结果的时候才会执行 using System; using System.C

  • c# Linq常用的小技巧

    前言 在C#语言发展的历史长河中,Linq是一个极其重要的里程碑! Linq的语法吸取了SQL语法的特性,同时配合Lambda表达式又可以使代码更加优雅! 可以这么说,用好了Linq可以大大提高程序猿的工作效率,毕竟我们的日常工作本质就是对数据的处理.经历了十多年的发展,现在微软自带的内库包含的Linq函数已经非常多了,几乎满足我们日常工作. 下面根据一个对科室数据操作的例子,就个人觉得日常高频使用的Linq小技巧贴出来,权当是做个笔记了. 初始化数据 定义模型 这里定义一个科室对象,模拟我们日

  • 分享11个常用JavaScript小技巧

    目录 1.通过条件判断向对象添加属性 2.检查对象中是否存在某个属性 3.解构赋值 4.循环遍历一个对象的key和value 5.使用可选链(Optionalchaining)避免访问对象属性报错 6.检查数组中falsy的值 7.数组去重 8.检查是否为数组类型 9.数字&字符串类型转换 10.巧用空值合并(??) 11.通过!!进行布尔转换 在我们的日常开发过程中,我们经常会遇到数字与字符串转换,检查对象中是否存在对应值,条件性操作对象数据,过滤数组中的错误值,等等这类处理. 在这里,整理出

  • PHP常用函数小技巧

    1. 返回文件扩展名 function getformat($file) { $ext=strrchr($file,"."); $format=strtolower($ext); return $format; } 2.格式化变量 <? $num = 1; printf("%04d", $num); ?> 3.php重定向网页 // 例如重定向到www.cgsir.com (注意重定向之前不要有html内容) header("location:

  • sqlserver 增删改查一些不常用的小技巧

    一 Insert 语句 1.在数据中只插入默认值: insert into [DBTrain].[dbo].[log_info] default values 2.在可以为null 的字段中,如果要设置null,可以按下[Ctrl+0],注意是零不是o,如果要恢复修改前的值按[Esc] 二 Update 语句 可以更新前几条或某个百分比的数据 update top(3) [log_info] set info1 = '1001' update top(80) percent [log_info]

  • Python常用小技巧总结

    本文实例总结了Python常用的小技巧.分享给大家供大家参考.具体分析如下: 1. 获取本地mac地址: import uuid mac = uuid.uuid1().hex[-12:] print(mac) 运行结果:e0cb4e077585 2. del 的使用 a = ['b','c','d'] del a[0] print(a)# 输出 ['c', 'd'] a = ['b','c','d'] del a[0:2] # 删除从第1个元素开始,到第2个元素 print(a)# 输出 ['d

  • ASP.NET常用小技巧

    今天为大家介绍6个ASP.NET常用技巧,使用操作简单,具有很高的实用性,记得收藏哦 1.跟踪页面执行  设置断点是页面调试过程中的常用手段,除此之外,还可以通过查看页面的跟踪信息进行错误排查以及性能优化.ASP.NET中启用页面跟踪非常方便,只需在Page指令中加入Trace="True"属性即可:设置断点是页面调试过程中的常用手段,除此之外,还可以通过查看页面的跟踪信息进行错误排查以及性能优化.ASP.NET中启用页面跟踪非常方便,只需在Page指令中加入Trace="T

  • PHP网站开发中常用的8个小技巧

    PHP是一种用于创建动态WEB页面的服务端脚本语言.如同ASP和ColdFusion,用户可以混合使用PHP和HTML编写WEB页面,当访 问者浏览到该页面时,服务端会首先对页面中的PHP命令进行处理,然后把处理后的结果连同HTML内容一起传送到访问端的浏览器.但是与ASP或 ColdFusion不同,PHP是一种源代码开放程序,拥有很好的跨平台兼容性.用户可以在Windows NT系统以及许多版本的Unix系统上运行PHP,而且可以将PHP作为Apache服务器的内置模块或CGI程序运行. 本

  • Javascript常用小技巧汇总

    本文实例讲述了Javascript常用小技巧.分享给大家供大家参考.具体分析如下: 一.True 和 False 布尔表达式 下面的布尔表达式都返回 false: null undefined '' 空字符串 0 数字0 但小心下面的, 可都返回 true: '0' 字符串0 [] 空数组 {} 空对象 下面段比较糟糕的代码: 复制代码 代码如下: while (x != null) { 你可以直接写成下面的形式(只要你希望 x 不是 0 和空字符串, 和 false): 复制代码 代码如下:

  • Android ListView常用小技巧汇总

    ListView在我们Android项目中的地位是有目共睹的,相信几乎每一个App中都有它的身影. ListView主要是用列表形式来加载数据,在特定情况下需要实现一些特殊功能:如刷新数据,加载数据,实现动画效果等. 作为我们常用的控件,有哪些需要注意的呢? **为ListView的每一Item设置分隔线 第一种方法:也是最简单地方法,在布局文件中设置ListView的 divider属性 如:android:divider="@color/black" 第二种方法:设置android

  • ES6常用小技巧总结【去重、交换、合并、反转、迭代、计算等】

    本文实例讲述了ES6常用小技巧.分享给大家供大家参考,具体如下: 1- 数组去重 var arr = [1,2,3,4,3,4]; var arr2 = [...new Set(arr)]; 这个时候arr2就是去重后的数组~ 2- 交换两个变量的值 let [x,y] = [1,2]; [y,x] = [x,y]; console.log(y); 3- 获取字符串中的某个字符 let arr= "hellomybo"; console.log(arr[3]); 4- 使用箭头函数代替

随机推荐