Компилятор C # позаботится о lot для вас, когда он превратит ваш итератор в реальный код. Например, вот MoveNext
, который содержит реализацию вашего второго примера 1 :
private bool MoveNext()
{
try
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<sillier>5__1 = this.source.GetEnumerator();
this.<>1__state = -3;
while (this.<sillier>5__1.MoveNext())
{
this.<>2__current = this.<sillier>5__1.Current;
this.<>1__state = 1;
return true;
Label_005A:
this.<>1__state = -3;
}
this.<>m__Finally1();
this.<sillier>5__1 = null;
return false;
case 1:
goto Label_005A;
}
return false;
}
fault
{
this.System.IDisposable.Dispose();
}
}
Итак, вы заметите, что предложение finally
из вашего using
вообще отсутствует, и это конечный автомат 2 , который полагается на то, что он находится в определенном состоянии (> = 0 ) государства для дальнейшего продвижения вперед. (Это также незаконно C #, но эй хо).
Теперь давайте посмотрим на его Dispose
:
[DebuggerHidden]
void IDisposable.Dispose()
{
switch (this.<>1__state)
{
case -3:
case 1:
try
{
}
finally
{
this.<>m__Finally1();
}
break;
}
}
Таким образом, мы видим, что здесь вызывается <>m__Finally1
(а также из-за выхода из цикла while
в MoveNext
.
А <>m__Finally1
:
private void <>m__Finally1()
{
this.<>1__state = -1;
if (this.<sillier>5__1 != null)
{
this.<sillier>5__1.Dispose();
}
}
Итак, мы можем видеть, что sillier
был утилизирован и мы перешли в отрицательное состояние, что означает, что MoveNext
не нужно выполнять никакой специальной работы для обработки «мы уже были уничтожены».
Итак,
Идея: возможен разрыв доходности if (disposed); после каждого возврата доходности. Теперь метод dispose для глупого перечислителя должен будет просто установить disposed = true и переместить перечислитель один раз, чтобы избавиться от всех необходимых вещей.
Совершенно не нужно. Доверяйте компилятору преобразовывать код так, чтобы он выполнял все логические вещи, которые он должен - он просто запускает свой оператор finally один раз, когда он либо исчерпал логику итератора, либо когда он явно расположен.
1 Все примеры кода, созданные .NET Reflector. Но сейчас слишком хорошо декомпилировать эти конструкции, так что если вы посмотрите на сам метод Silly
:
[IteratorStateMachine(typeof(<Silly>d__1)), Extension]
private static IEnumerable<T> Silly<T>(this IEnumerable<T> source)
{
IEnumerator<T> <sillier>5__1;
using (<sillier>5__1 = source.GetEnumerator())
{
while (<sillier>5__1.MoveNext())
{
yield return <sillier>5__1.Current;
}
}
<sillier>5__1 = null;
}
Ему удалось скрыть большинство деталей об этом автомате. Вам нужно преследовать тип, на который ссылается атрибут IteratorStateMachine
, чтобы увидеть все биты, показанные выше.
2 Обратите также внимание, что компилятор не имеет никаких обязательств , чтобы создать конечный автомат для работы итераторов. Это деталь реализации текущих компиляторов C #. Спецификация C # не накладывает никаких ограничений на как компилятор преобразует итератор, только в том, какими должны быть эффекты.