Странный порядок выполнения при использовании вложенного метода, возврат доходности и использование в комбинации
Я не могу понять, почему 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
будет фактически выполнен. В реальной программе вы получите ошибки, потому что ваш контекст уже настроен.