首页 > 解决方案 > 继续枚举 IEnumerable

问题描述

使用yield关键字,我们只能计算IEnumerable. 我已经设置了一个测试,其中我生成了一些项目,然后我想再生成一个。我所期望的是函数从它结束的地方开始,在这种情况下是在10000000迭代中并且只迭代一次到10000001. 请查看这段代码:

public static void Main()
{
    var naturalNumbers = GetNaturalNumbers();

    var executionTime = GetExecutionTime(() => naturalNumbers.ElementAt(10000000));
    Console.WriteLine($"Time elapsed: {executionTime}");

    executionTime = GetExecutionTime(() => naturalNumbers.ElementAt(10000001));
    Console.WriteLine($"Time elapsed: {executionTime}");
}

public static IEnumerable<int> GetNaturalNumbers()
{
    Console.WriteLine("Running GetNaturalNumbers() from the beginning");
    for(int value = 0;; value++)
    {
        yield return value;
    }
}

public static System.TimeSpan GetExecutionTime(Action action)
{
    var stopwatch = Stopwatch.StartNew();

    action();

    stopwatch.Stop();
    return stopwatch.Elapsed;
}

输出:

Running GetNaturalNumbers() from the beginning
Time elapsed: 00:00:00.0618548
Running GetNaturalNumbers() from the beginning
Time elapsed: 00:00:00.0562454

第二次调用所用的时间较短,这让我猜测这只是因为处理器优化。该函数从一开始就被调用了两次。

这个输出背后的真正原因是什么?是否可以从我们在第一次调用结束时继续迭代?

编辑:

现在我发现我的例子还不够好。假设我们在函数中有一个非常复杂且耗时的操作GetNaturalNumbers()。我期待第二次调用从10000000'th 元素开始枚举,而不需要再次计算所有元素。我预计第二次通话会有显着的性能提升。


另一个例子:

想象一本有很多页的书,您不想一次获得所有页面,而只想获得读者需要的数量。当读者翻页时,我们会得到它,而无需计算所有以前的。

标签: c#

解决方案


其他人似乎都在谈论速度,这似乎不是你真正的问题。

您正在考虑执行后:

var executionTime = GetExecutionTime(() => naturalNumbers.ElementAt(10000000));

然后执行:

executionTime = GetExecutionTime(() => naturalNumbers.ElementAt(10000001));

它应该知道你已经这样做了,10000000所以它应该只做 1 次循环迭代。

这根本不是它的工作原理。您的 yield return 将缩短循环并返回您需要的值,但是这两个语句是完全独立的,它将运行:

for(int value = 0;; value++)

两次。


推荐阅读