Странный порядок выполнения при использовании вложенного метода, возврат доходности и использование в комбинации

Я не могу понять, почему Program.Fetch1 и Program.Fetch2 не соответствуют точному порядку выполнения. Единственное отличие состоит в том, что Program.Fetch1 вызывает Program.Fetch для выполнения фактической операции выборки.

class Program
{
    static IEnumerable<int> Fetch1()
    {
        using (Context c = new Context())
        {
            return Fetch(c);
        }
    }

    static IEnumerable<int> Fetch(Context c)
    {
        foreach (int i in c.Fetch())
        {
            yield return i;
        }
    }

    static IEnumerable<int> Fetch2()
    {
        using (Context c = new Context())
        {
            foreach (int i in c.Fetch())
            {
                yield return i;
            }
        }
    }

    static void Main(string[] args)
    {
        Console.WriteLine("Fetch1:");
        foreach (int i in Fetch1())
        {
            Console.WriteLine(i);
        }
        Console.WriteLine("Fetch2:");
        foreach (int i in Fetch2())
        {
            Console.WriteLine(i);
        }
    }
}


class Context : IDisposable
{

    public void Dispose()
    {
        Console.WriteLine("Context.Dispose");
    }

    public IEnumerable<int> Fetch()
    {
        return new int[] { 1, 2 };
    }
}

Вывод:

Fetch1:
Context.Dispose
1
2
Fetch2:
1
2
Context.Dispose

Мое единственное предположение, что Context.Dispose вызывается сначала в Program.Fetch1, потому что область применения объявления объявления уже оставлена. Но это верно и для Program.Fetch1. Так почему же этот метод ведет себя по-другому?

Обновление: Мой вопрос - это дубликат return return внутри блока using() {}. Перед выполнением выполняется

Ответ 1

Это потому, что эти параметры действительно разные:

  • Fetch и Fetch2, используя yield, создайте конечный автомат, чтобы иметь возможность вернуть не материализуемый IEnumerable.
  • В Fetch1 вы просто вызываете Fetch и возвращаете сгенерированный конечный автомат и удаляете контекст, не ожидая, что этот IEnumerable будет фактически реализован.

В основном разница заключается в том, что в Fetch2 у вас есть слой отложенного исполнения (с использованием yield), а в Fetch1 - нет, что означает, что область использования заканчивается сразу же при возврате.

Ответ 2

Ваш метод Fetch возвращает IEnumerable. Когда это возвращается, ваш используемый блок использует контекст. Однако IEnumerable - это только обещание исполнения. Он будет выполнен. Когда вы перечислите его. Это называется отложенным исполнением.

Итак, когда вы на самом деле перечисляете его своим foreach, перечисление будет иметь место, и код Fetch будет фактически выполнен. В реальной программе вы получите ошибки, потому что ваш контекст уже настроен.