TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.77K subscribers
Post #972 1.34K
День семьсот девяностый. #ЗаметкиНаПолях
Мои любимые ошибки с IDisposable. 2/3
Начало

3. Проблемы using и yield
IDisposable и using дают нам инструменты для управления очисткой наших ресурсов, но это может противоречить ленивой оценке, которую мы получаем от ключевого слова yield.

Давайте возьмём простой пример и воспользуемся Console.WriteLine, чтобы увидеть, как проявляется ошибка. Нам понадобится реализация IDisposable и несколько вспомогательных функций, чтобы реализовать неявный вызов. Сначала рассмотрим рабочий код:
https://dotnetfiddle.net/WwiN0r

Когда мы выполняем этот код, мы получаем ожидаемый результат:
start
connected
querying
dispose
result: 1
done

Все операции расположены в правильном порядке, и мы очищаем наши ресурсы сразу после использования. Заменим вывод метода Query в строке 17 на yield return:
https://dotnetfiddle.net/sfF1Au

Теперь результат довольно странный:
start
connected
dispose
querying
result: 1
done
Наш запрос выполняется ПОСЛЕ очистки ресурсов! Заметьте, что в реальном приложении вы получите ошибку при обращении к несуществующему ресурсу. Что происходит?

Мы просто добавили yield return, но компилятор «под капотом» проделал намного больше работы. Он создал класс с уникальным именем, реализующий IEnumerable<int>. Наш запрос к FakeResource возвращает экземпляр этого класса, но мы не выполняем собственно запроса, пока не начнёт исполняться цикл foreach. У Джона Скита есть отличный разбор всех внутренностей реализации. А подробнее про особенности выражения yield return, вы можете почитать на канале. Поищите посты по строке "Выражение yield return".

Главное отличие здесь в том, что мы перешли от немедленного оценивания выражения к ленивому, и это изменило реальный порядок действий: мы покинули блок using перед запуском нашего кода в Query(FakeResource).

Эта ошибка замечательна, потому что yield return и using могут быть в совершенно разных частях проекта. А чтобы столкнуться с этой ошибкой, нам достаточно хотя бы одного уровня косвенного доступа, и это довольно распространённое явление. При этом изменение на yield return само по себе выглядит невинно, и компилятор всем доволен.

Этот вид ошибки также может быть создан с помощью async и await; мы можем покинуть блок using, пока задача всё ещё хочет использовать наш IDisposable (см. этот пост).

Окончание следует…

Источник:
https://www.lazy-electron.com/2021/03/06/favorite-idisposable-bugs.html
Автор оригинала - Ryan Davis
More from @netdeveloperdiary
  1. Oct 11, 2026День 2811. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
  2. Oct 10, 2026День 2810. #ЧтоНовенького #VSCode Более Быстрый и Лёгкий C# Dev Kit Мы, разработчики, люби…
  3. Oct 9, 2026День 2809. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Окончание Начало 3.…
  4. Oct 8, 2026День 2808. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Начало В ИТ есть се…
  5. Oct 7, 2026День 2807. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Окончание Нач…
  6. Oct 6, 2026🦈 Открытое собеседование на Middle C# | 6 октября, 19:00 МСК Приглашаем на открытое собес…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →