ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Linq下有一个非常实用的SelectMany方法,很多人却不会用

2020-08-30 07:00:58  阅读:222  来源: 互联网

标签:TagCrowdFilterModel 下有 Linq selector foreach new SelectMany public CrowdFiter


在平时开发中经常会看到有些朋友或者同事在写代码时会充斥着各种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才能搞定,再放张图给大家看看,是不是看着头大...
Linq下有一个非常实用的SelectMany方法,很多人却不会用

3. 优化办法

如果你会selectMany,那只需要一个链式写法就可以搞定,是不是简单粗暴,虽然性能比不上命令式写法,但可读性和观赏性真的上了几个档次。

        estimateModel.YearCrowdFilterDict.SelectMany(m => m.Value).ToList().ForEach(m =>
            m.TotalCustomerCount = GetCustomerID(m.CrowdFiter)
        );

二:原理探究

1. msdn解释

将序列的每个元素投影到 IEnumerable,并将结果序列合并为一个序列,并对其中每个元素调用结果选择器函数,链接: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,这得益于yield语法糖,它本质上就是一个编译器封装好的迭代类,做到了一个延迟,按需执行,后面我会专门分享一下yield,很有意思

好了,本篇就说到这里,希望对你有帮助。

标签:TagCrowdFilterModel,下有,Linq,selector,foreach,new,SelectMany,public,CrowdFiter
来源: https://blog.51cto.com/huangxincheng/2525763

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有