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小技巧的资料请关注猪先飞其它相关文章!
相关文章
- 我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
- 这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
- 这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
- 本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
- 这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
- 本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)
这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25- 这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
- 这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
- 最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
- 本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
- 轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
- 本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
- 这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
- 本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
- 这篇文章主要介绍了c#自带缓存使用方法,包括获取数据缓存、设置数据缓存、移除指定数据缓存等方法,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
- 这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
- 下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
- 这篇文章主要介绍了C#中list用法,结合实例形式分析了C#中list排序、运算、转换等常见操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25