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,說實話我沒有搞清楚為什么這里需要加。還望各位給我指點迷津。

        最后感謝韋恩卑鄙,沒有這個胖胖就沒有這篇文章。哈哈。

        本篇相關代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM