При использовании "yield", почему генерируемый компилятором тип реализует как IEnumerable, так и IEnumerator

Мы пытаемся использовать IEnumerable как factory, который генерирует разные объекты каждый раз, когда мы перебираем его. Те должны быть GC'ed как можно скорее. Обратите внимание, однако, что мы сохраняем ссылку на перечислитель, чтобы мы могли снова позвонить ему. Поэтому наша программа в основном выглядит так:

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 должен генерировать только IEnumerator при вызове GetEnumerator. Является ли это ожидаемым поведением, что IEnumerable может содержать состояние?

Ответ 1

В качестве детализации реализации по соображениям производительности да, государственный аппарат реализует как IEnumerable, так и IEnumerator. Тем не менее, он достаточно умен, чтобы сделать это правильно. Только в первый раз, когда IEnumerable запрашивается IEnumerator, он возвращается сам. Любые последующие вызовы GetEnumerator приводят к созданию нового экземпляра создаваемого объекта, так что могут поддерживаться отдельные состояния итератора. Это делается потому, что, хотя важно иметь возможность IEnumerable создавать несколько IEnumerators, подавляющее большинство реальных ситуаций включает ровно одно созданное, чтобы оптимизированная ситуация.