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

Joh*_*ger 10 .net c# linq garbage-collection yield-return

我们正在尝试使用a 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);
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

在调试器中查看该代码:

在此输入图像描述

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

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

Ser*_*rvy 8

作为性能原因的实现细节,是的,状态机实现了IEnumerable和IEnumerator.也就是说,正确地做到这一点非常聪明.这是第一次IEnumerable被要求IEnumerator它返回自己.将来调用会GetEnumerator导致创建对象的新实例,以便可以维护单独的迭代器状态.这样做是因为虽然能够拥有一个IEnumerable创建倍数很重要IEnumerators,但绝大多数实际情况只涉及一个被创建,因此这是优化的情况.

  • @bradgonesurfing:为了澄清这个正确的答案,这里的"性能原因"是*降低GC压力*.您可以很容易地想象这样一种情况,即包含`yield`s的这些方法每秒被调用数千次并立即枚举.如果我们可以减少创建新对象数量的一半,那么这是一个明显的胜利. (2认同)