Ответ вслух Middle Делегаты и события обновлено

Замыкания в C#

Вопрос

Объясните, что такое замыкание в C# и какие с ним бывают проблемы.

Попробуйте сами

Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.

Показать ответ и разбор

Коротко

Замыкание это функция вместе с переменными внешнего контекста, которые она использует. В C лямбда захватывает именно переменную, а не её значение в момент создания: если переменную потом изменить, лямбда увидит новое значение.

Что должно прозвучать в ответе

  • Лямбда захватывает переменные, а не значения

  • Компилятор создаёт скрытый класс с полями для захваченных переменных

  • Проблема цикла for: одна переменная на все итерации

  • Захват продлевает жизнь объектов, может вызвать утечку

  • Аллокации на каждый вызов, static-лямбды запрещают захват

Что могут спросить следом

  • Почему foreach с C# 5 ведёт себя иначе, чем for?
  • Что будет, если захватить this в долгоживущем колбэке?

Разбор

Замыкание это функция вместе с переменными внешнего контекста, которые она использует. В C# лямбда захватывает именно переменную, а не её значение в момент создания: если переменную потом изменить, лямбда увидит новое значение.

Технически компилятор создаёт скрытый класс, переносит туда захваченную переменную полем, и и внешний метод, и лямбда работают с этим полем. Отсюда классическая ловушка: в цикле for переменная одна на весь цикл, и все лямбды увидят её последнее значение. В foreach с C# 5 переменная заводится заново на каждой итерации, поэтому там проблемы нет.

Вторая проблема: захваченный объект живёт, пока жив делегат. Если колбэк захватил this и отдан в долгоживущий сервис, объект не соберётся. Третья: каждое создание лямбды с захватом это аллокация. В горячем коде можно пометить лямбду static, тогда компилятор запретит захват.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.

Термины по теме