• Linq使用心得——伪造一个SelectMany


            上篇《Linq使用心得——SelectMany替代二重foreach循环》中我们学习了SelectMany的一些用法。不小心给韦恩卑鄙这个家伙看到了,他就唆使我写如何伪造一个SelectMany方法。这真是赶鸭子上架啊,所以今天我们就来试试看吧。其实也没啥好说的,直接上代码吧。

        static class FakeLinq
        {
            public static IEnumerable<TResult> FakeSelectMany<TSource, TResult>(
                this IEnumerable<TSource> source,Func<TSource, IEnumerable<TResult>> selector)
            {
                foreach (var s in source)
                {
                    foreach (var r in selector(s))
                    {
                        yield return r;
                    }
                }
            }
    
            public static IEnumerable<TResult> FakeSelectMany<TSource, TResult>(
                this IEnumerable<TSource> source,Func<TSource, int, IEnumerable<TResult>> selector)
            {
                int index = 0;
                foreach (var s in source)
                {
                    foreach (var r in selector(s,index++))
                    {
                        yield return r;
                    }
                }
            }
    
            public static IEnumerable<TResult> FakeSelectMany<TSource, TCollection, TResult>(
                this IEnumerable<TSource> source,
                Func<TSource, IEnumerable<TCollection>> collectionSelector,
                Func<TSource, TCollection, TResult> resultSelector)
            {
                foreach (var s in source)
                {
                    foreach (var c in collectionSelector(s))
                    {
                        yield return resultSelector(s, c);
                    }
                }
            }
    
            public static IEnumerable<TResult> FakeSelectMany<TSource, TCollection, TResult>(
                this IEnumerable<TSource> source,
                Func<TSource, int, IEnumerable<TCollection>> collectionSelector,
                Func<TSource, TCollection, TResult> resultSelector)
            {
                int index = 0;
                foreach (var s in source)
                {
                    foreach (var c in collectionSelector(s,index++))
                    {
                        yield return resultSelector(s, c);
                    }
                }
            }
        }

            我们来试试效果,发现用起来是完全一样的感觉:

                List<Teacher> teachers = new List<Teacher> 
                {
                    new Teacher("a",new List<Student>{ new Student(100),new Student(90),new Student(30) }),
                    new Teacher("b",new List<Student>{ new Student(100),new Student(90),new Student(60) }),
                    new Teacher("c",new List<Student>{ new Student(100),new Student(90),new Student(40) }),
                    new Teacher("d",new List<Student>{ new Student(100),new Student(90),new Student(60) }),
                    new Teacher("e",new List<Student>{ new Student(100),new Student(90),new Student(50) }),
                    new Teacher("f",new List<Student>{ new Student(100),new Student(90),new Student(60) }),
                    new Teacher("g",new List<Student>{ new Student(100),new Student(90),new Student(60) })
                };
    
                var list1 = teachers.SelectMany(t => t.Students).Where(s => s.Score < 60).ToList();
                var list2 = teachers.FakeSelectMany(t => t.Students).Where(s => s.Score < 60).ToList();
    
                var list3 = teachers.SelectMany(
                    t => t.Students,
                    (t, s) => new { t.Name, s.Score })
                    .Where(n => n.Score < 60).ToList();
                var list4 = teachers.FakeSelectMany(
                    t => t.Students,
                    (t, s) => new { t.Name, s.Score })
                    .Where(n => n.Score < 60).ToList();

            是不是有种微软就是个骗子,原来这么简单的感觉?其实也没那么简单,在完成上述代码之后,我又用ILSpy去看了微软SelectMany的实现,发现主要有以下3点区别:

            1.我没有对传入的参数做任何的有效性检测,这在平时做应用开发时可能不是大问题,但如果是写一个通用类库供他人使用是绝对不能缺少的:

        if (source == null)
        {
            throw Error.ArgumentNull("source");
        }
        if (selector == null)
        {
            throw Error.ArgumentNull("selector");
        }

            2.存在第二个int index参数的情况下,我们没有对index进行溢出的检测:

        int num = -1;
        checked
        {
            foreach (TSource current in source)
            {
                num++;
                foreach (TResult current2 in selector(current, num))
                {
                    yield return current2;
                }
            }
            yield break;
        }

            3.第三点就是上面的这个yield break,说实话我没有搞清楚为什么这里需要加。还望各位给我指点迷津。

            最后感谢韦恩卑鄙,没有这个胖胖就没有这篇文章。哈哈。

            本篇相关代码

  • 相关阅读:
    Docker容器查看ip地址
    抽象工厂模式
    idea插件
    作业统计
    tarjan强连通图分量
    Android动画浅析
    位运算
    mongodb笔记
    依赖倒置原则
    单一职责原则
  • 原文地址:https://www.cnblogs.com/manupstairs/p/2793384.html
Copyright © 2020-2023  润新知