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

Will C# Take(k) extension method execute a complete previous GroupBy in sequence?

我 "playing" 使用 LINQ 并测试了一些东西,一些东西引起了我的注意。

假设我有 GroupBy 扩展方法的 "lazy" 实现:

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 之前完全执行。我不知道这是我的实现方式还是以某种方式 original Take 做了一些不同的事情。

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

的一个实例

Will .Take(2) force the complete .GroupByA(x=>x.Group) execution

查看完整的 students.GroupByA(x => x.Group).Take(2) 表达式,.Net 能够使用由 GroupByA() 创建的新 class 实例和 Take() 函数,您可以将其视为执行仅持续到您的代码第二次到达 yield 行,但不会继续执行。

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

应该可以使用字典而不是列表来编写 O(n) GROUP BY 操作,以查找新组并在字典值中累积聚合信息。你可能想看看你是否能做到这一点。当然,问题在于 n 的值较小(源列表大小较小),哈希计算和查找的成本可能高于序列迭代。