使用 "yield" 时,为什么编译器生成的类型同时实现 IEnumerable 和 IEnumerator

When using "yield" why does compiler-generated type implement both IEnumerable and IEnumerator

我们正在尝试使用 IEnumerable 作为工厂,每次我们对其进行迭代时都会生成不同的对象。这些应该尽快进行 GC。但是请注意,我们 保留对枚举器 的引用,以便我们可以再次调用它。所以我们的程序基本上是这样的:

public class YieldSpec
{
    public static IEnumerable<string> Strings()
    {
        yield return "AAA";
        yield return "BBB";
        yield return "CCC";
    } 
    public void YieldShouldAllowGC()
    {
        var e = Strings();
        foreach (var a in e)
        {
            Console.WriteLine(a);
        }

    }
}

在调试器中查看该代码:

您可以看到,当遇到断点时,IEnumerable 引用了 "CCC"

这真的不应该发生。 IEnumerable 应该只在调用 GetEnumerator 时生成一个 IEnumerator。这是 IEnumerable 可以包含状态的预期行为吗?

作为出于性能原因的实现细节,是的,状态机同时实现了 IEnumerableIEnumerator。也就是说,它足够聪明,可以正确地做到这一点。这只是第一次 IEnumerable 被要求 IEnumerator 而它 returns 本身。以后对 GetEnumerator 的任何调用都会导致创建对象的新实例,以便可以维护单独的迭代器状态。这样做是因为虽然 能够 有一个 IEnumerable 创建多个 IEnumerators 很重要,但 广大 多数的实际情况正好涉及一个正在创建的情况,因此这就是针对这种情况进行了优化。