C#Take(k)扩展方法会按顺序执行完整的前一个GroupBy吗?

时间:2018-03-21 14:19:43

标签: c# linq iterator extension-methods

我是"在玩#34;在LINQ附近测试一些东西,引起了我的注意。

假设我有这个"懒惰" GroupBy扩展方法的实现:

public static IEnumerable<IGrouping<TKey, TSource>> GroupByA<TSource, TKey>(
        this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector)
    {
        //To avoid duplicate groups
        List<TKey> grouping = new List<TKey>();
        foreach (var item in source)
        {
            if (!grouping.Contains(keySelector(item)))
            {
                grouping.Add(keySelector(item));
                Group<TKey, TSource> g = new Group<TKey, TSource>(
                    keySelector(item),
                    source.Where(x => keySelector(x).Equals(keySelector(item)))
                );
                Console.WriteLine("Returning group");
                yield return g; //yield returning a complete group
            }
        }
    }

注意:假设Group<TKey, TSource>实施IGrouping<TKey, TSource

我想知道,如果执行此操作会发生什么?

var groups = students.GroupByA(x => x.Group).Take(2);

注意:studentsList<Student>

.Take(2)会强制完成.GroupByA(x=>x.Group)执行还是以某种方式一次消耗一个组,直到它计算为2无论哪种方式为什么吗

PS:我尝试使用自己的实现:

public static IEnumerable<T> TakeA<T>(this IEnumerable<T> source, int count)
像这样:

 public static IEnumerable<T> TakeA<T>(this IEnumerable<T> source, int count)
    {
        int iter = 0;
        foreach (var item in source)
        {
            if (iter == count)
                yield break;
            yield return item;
            iter++;
        }
    }

但我很确定这种方式会导致GroupBy在调用TakeA之前完全执行。我不知道这是我实现它的方式还是以某种方式原始 Take做了别的其他事情。

1 个答案:

答案 0 :(得分:1)

C#编译器将您的代码转换为状态机。也就是说,它会在幕后创建一个新类,其中包含迭代学生列表所需的状态和行为。每次调用代码时,都会得到此类的实例。

  

将.Take(2)强制完成.GroupByA(x =&gt; x.Group)执行

查看完整的students.GroupByA(x => x.Group).Take(2)表达式,.Net能够使用由GroupByA()创建的Take()函数创建的新类实例,并且您可以将其视为仅执行继续,直到您的代码第二次到达yield行,但没有进一步。

但是,GROUP BY操作的性质是您必须循环遍历整个数据集以了解组的属性,这意味着即使您只看到第二个yield表达式,source.Where()调用仍然需要查看整个数据集,并至少进行O(n*m)操作...每次识别新组时,都会再次浏览整个数据集。

应该可以使用Dictionary而不是List来编写O(n) GROUP BY操作来查找新组并在字典值中累积聚合信息。您可能想看看是否可以管理它。当然,捕获的n(小源列表大小)值很小,哈希计算和查找的成本可能高于序列迭代。