Linq下有一个非常实用的SelectMany方法,很多人却不会用
在平时开发中经常会看到有些朋友或者同事在写代码时会充斥着各种for,foreach,这种程式代码太多的话阅读性特别差,而且还显得特别累赘,其实在FCL中有很多帮助我们提高阅读感的方法,而现实中很多人不会用或者说不知道,这篇我就跟大家聊一聊。
一:SelectMany
这个方法绝对是提高开发速度的一大利器,有太多的业务场景需要使用这个函数,举一个我实际应用场景,商家按照年份和客户类型预先设置一些标签,然后让系统跑一下它的各自标签到底有多少人?
1. 定义Model
为了方便演示,这里做了一下简化代码,只有一个字典,key表示年份,value:就是该年份的多组标签。
public class EstimateModel { public int ShopID { get; set; } //key: 年份 public Dictionary<string, List<TagCrowdFilterModel>> YearCrowdFilterDict { get; set; } } public class TagCrowdFilterModel { /// <summary> /// 筛选条件 /// </summary> public string CrowdFiter { get; set; } /// <summary> /// 获取人数 /// </summary> public int TotalCustomerCount { get; set; } }
为了更加清晰,我决定再填充一下数据
public static void Main(string[] args) { var estimateModel = new EstimateModel() { ShopID = 1, YearCrowdFilterDict = new Dictionary<string, List<TagCrowdFilterModel>>() { { "17年",new List<TagCrowdFilterModel>() { new TagCrowdFilterModel(){ CrowdFiter="between 10 and 20" }, new TagCrowdFilterModel(){ CrowdFiter=" a<10 || a>30" }, } }, { "18年",new List<TagCrowdFilterModel>() { new TagCrowdFilterModel(){ CrowdFiter="between 100 and 200" }, new TagCrowdFilterModel(){ CrowdFiter=" a<100 || a>300" }, } }, { "19年",new List<TagCrowdFilterModel>() { new TagCrowdFilterModel(){ CrowdFiter="between 1000 and 2000" }, new TagCrowdFilterModel(){ CrowdFiter=" a<1000 || a>3000" }, } } } }; } public static int GetCustomerID(string crowdfilter) { return BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0); }
2. 实现需求
需求也很简单,就是依次获取 TagCrowdFilterModel 中的 CrowdFiter 字段再调用GetCustomerID方法把人数赋值给TotalCustomerCount即可,这么简单的需求,如果让你来搞定,你该怎么实现这个逻辑? 没错,很多人可能就是两个foreach搞定。
foreach (var year in estimateModel.YearCrowdFilterDict.Keys) { var yearCrowdFitlerList = estimateModel.YearCrowdFilterDict[year]; foreach (var crowdFitler in yearCrowdFitlerList) { crowdFitler.TotalCustomerCount = GetCustomerID(crowdFitler.CrowdFiter); } }
看似代码也很清爽,但现实哪有这么好的事情,真实情况是年份上可能还要套上一个客户类型,客户类型之上再套一个商品,商品之上再套一个商家,这样很深的层级你就需要多达3个foreach,4个foreach甚至5个foreach才能搞定,再放张图给大家看看,是不是看着头大...😄
3. 优化办法
如果你会selectMany,那只需要一个链式写法就可以搞定,是不是简单粗暴,虽然性能比不上命令式写法,但可读性和观赏性真的上了几个档次。
estimateModel.YearCrowdFilterDict.SelectMany(m => m.Value).ToList().ForEach(m => m.TotalCustomerCount = GetCustomerID(m.CrowdFiter) );
二:原理探究
1. msdn解释
将序列的每个元素投影到 IEnumerable<T>,并将结果序列合并为一个序列,并对其中每个元素调用结果选择器函数,链接: https://docs.microsoft.com/zh-cn/dotnet/api/system.linq.enumerable.selectmany?view=netframework-4.8 有了上面的案例解释,再看msdn的这句话,我想你应该彻彻底底的明白了selectMany怎么使用。
2. 翻查源码
宏观上明白了,接下来用ILSpy去查下微观代码,到底这玩意是怎么实现的。
public static IEnumerable<TResult> SelectMany<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector) { if (source == null) { throw Error.ArgumentNull("source"); } if (selector == null) { throw Error.ArgumentNull("selector"); } return SelectManyIterator(source, selector); } private static IEnumerable<TResult> SelectManyIterator<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector) { foreach (TSource item in source) { foreach (TResult item2 in selector(item)) { yield return item2; } } }
大家仔细体会下这两个foreach,尤其是第二个foreach,其中的selector(item)不就是年份下的标签集合吗?再遍历这个集合把每一个item返回出去,返回值是IEnumerable<TResult>,这得益于yield语法糖,它本质上就是一个编译器封装好的迭代类,做到了一个延迟,按需执行,后面我会专门分享一下yield,很有意思😄
好了,本篇就说到这里,希望对你有帮助。
- 获取当天是星期几,非常方便实用的一个方法
- 【C#】对异步请求处理程序IHttpAsyncHandler的理解和分享一个易用性封装 【手记】走近科学之为什么明明实现了IEnumerable<T>的类型却不能调用LINQ扩展方法 【手记】手机网页弹出层后屏蔽底层的滑动响应 【手记】ASP.NET提示“未能创建类型”处理 【Web】一个非常简单的移动web消息框 【手记】解决EXCEL跑SQL遇“查询无法运行或数据库表无法打开...”
- asp.net 自己封装数据库操作一个类中一个自定义方法Execute(),非常实用,省去了麻烦的中间过程,动态参数
- Parallel.For 你可能忽视的一个非常实用的重载方法
- 南阳11 直接打印这是一个非常实用的方法,但细节的换行符要注意
- 一个简单不过却很非常实用的PHP加密字符串方法
- Parallel.For 你可能忽视的一个非常实用的重载方法
- sdcms的模板解析引擎,一个非常简单和实用的CMS
- 我们公司接到了一个比较大的IT软件项目,由于项目时间紧急,我们想采用有非常手段、非常方法,不自己进行全部的开发工作。
- 【LINQ-SQL Debug】 一个实用工具
- 一个非常强大的SSH分页方法
- 第一次安装android sdk后进行开发包的更新,你应该了解到需要的时间会很长,那么是否有办法提升安装的进度呢?办法自然是有的,这里提供一个取巧的方法,不会太麻烦,又能加快android开发环境的部
- 在 Linux 环境下,编写一个非常实用的删除小脚本
- 二叉数实现方法 非常绕的一个方法 递归
- 一个简单实用的保护视力的方法(IT人士必看的方法)
- 关于Linq的.distinct()方法的运用(一个简单的例子)
- 让程序只运行一个实例的简单实用的方法
- ARP欺骗查找主机方法(非常实用)
- Android开发中非常实用的方法,API等