Замыкания в C#
Вопрос
Объясните, что такое замыкание в C# и какие с ним бывают проблемы.
Попробуйте сами
Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.
Показать ответ и разбор
Коротко
Что должно прозвучать в ответе
Лямбда захватывает переменные, а не значения
Компилятор создаёт скрытый класс с полями для захваченных переменных
Проблема цикла for: одна переменная на все итерации
Захват продлевает жизнь объектов, может вызвать утечку
Аллокации на каждый вызов, static-лямбды запрещают захват
Что могут спросить следом
- Почему foreach с C# 5 ведёт себя иначе, чем for?
- Что будет, если захватить this в долгоживущем колбэке?
Разбор
Замыкание это функция вместе с переменными внешнего контекста, которые она использует. В C# лямбда захватывает именно переменную, а не её значение в момент создания: если переменную потом изменить, лямбда увидит новое значение.
Технически компилятор создаёт скрытый класс, переносит туда захваченную переменную полем, и и внешний метод, и лямбда работают с этим полем. Отсюда классическая ловушка: в цикле for переменная одна на весь цикл, и все лямбды увидят её последнее значение. В foreach с C# 5 переменная заводится заново на каждой итерации, поэтому там проблемы нет.
Вторая проблема: захваченный объект живёт, пока жив делегат. Если колбэк захватил this и отдан в долгоживущий сервис, объект не соберётся. Третья: каждое создание лямбды с захватом это аллокация. В горячем коде можно пометить лямбду static, тогда компилятор запретит захват.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.
Предыдущий вопрос
Добавление и удаление методов в делегате
Следующий вопрос
События, колбэки и их альтернативы