Во всех ответах до сих пор пропущена важная деталь: код в блоке finally
, который заключает в себе yield return
, будет выполнен, если и когда IDisposable.Dispose
вызывается для итератора / перечислителя, который выполнил yield return
. Если внешний код вызывает GetEnumerator()
на итераторе, а затем вызывает MoveNext()
, пока итератор не выполнит yield return
в блоке finally
, а внешний код затем покинет перечислитель без вызова Dispose
, код в finally
блок не будет работать. В зависимости от того, что делал итератор, он может быть уничтожен сборщиком мусора (хотя и не имеет возможности очистить какие-либо внешние ресурсы), или он может в конечном итоге окончательно или полупостоянно укорениться как утечка памяти (что может произойти, если например, он прикрепил лямбда-выражение к обработчику событий долгоживущего объекта).
Обратите внимание, что хотя и vb, и c # очень хороши для обеспечения того, чтобы циклы foreach
вызывали Dispose
для перечислителей, возможно использовать итераторы, явно вызывая GetEnumerator()
, и возможно, что некоторый код может сделать это без вызова Dispose()
. Итератор мало что может с этим поделать, но любой, кто пишет итераторы, должен знать о возможности.