Как в C# работает замыкание (closure) при захвате внешней переменной, например counter?

Как в C# устроено замыкание (closure) с внешней переменной замыкание — это сохранённый контекст окружения лямбды внешняя переменная (например, counter) захватывается по ссылке, а не копируется все обращения к этой…

Короткий ответ

Что ответить на собеседовании

Как в C# устроено замыкание (closure) с внешней переменной замыкание — это сохранённый контекст окружения лямбды внешняя переменная (например, counter) захватывается по ссылке, а не копируется все обращения к этой переменной из лямбды используют один экземпляр компилятор превращает переменную в поле сгенерированного класса изменения, внесённые после создания лямбды, отображаются при всех вызовах механизм сохраняет доступ к окружению даже после завершения метода часто применяется для хранения состояния в функциях и обработчиках событий

Подробный разбор

Ответ с пояснениями

Как в C# устроено замыкание (closure) с внешней переменной

  • замыкание — это сохранённый контекст окружения лямбды
  • внешняя переменная (например, counter) захватывается по ссылке, а не копируется
  • все обращения к этой переменной из лямбды используют один экземпляр
  • компилятор превращает переменную в поле сгенерированного класса
  • изменения, внесённые после создания лямбды, отображаются при всех вызовах
  • механизм сохраняет доступ к окружению даже после завершения метода
  • часто применяется для хранения состояния в функциях и обработчиках событий

Подробный ответ

Основной ответ

В C# замыкание (closure) — это механизм, благодаря которому лямбда-выражение или анонимный метод может захватывать переменные внешнего контекста и обращаться к ним после создания. Если лямбда использует переменную из внешнего метода, например counter, компилятор представляет её как поле специального вспомогательного объекта, известного как display class. Поэтому лямбда хранит ссылку на этот объект, а не отдельную копию значения переменной.

Следовательно, когда значение переменной counter меняется между вызовами лямбды, при следующем обращении используется её актуальное состояние. Все такие лямбды работают с одним и тем же объектом-хранилищем. Внутри C# это реализуется через скрытые классы: переменные становятся их полями, а лямбды — методами этих классов.

Ключевые моменты

  • Capture by reference: внешнее значение не дублируется; переменная захватывается по ссылке, поэтому её изменения доступны всем связанным замыканиям.
  • Display class: компилятор создаёт скрытый класс, размещает переменную в его поле и представляет лямбду как метод этого класса.
  • Память и время жизни: когда лямбда сохраняется, например возвращается из метода, объект display class продолжает существовать после завершения метода, что может отражаться на сборке мусора.

Практический контекст

На практике такой механизм встречается в счётчиках, делегатах и обработчиках событий, которым требуется «захватить» локальное состояние. Ленивое вычисление состояния в React 18+ напоминает замыкания, однако в C# эта конструкция позволяет удобно и безопасно работать с внешним состоянием, не передавая его явно через параметры. Понимание принципа важно, чтобы избегать проблем, связанных с изменяемым состоянием и многопоточностью.

Практика в реальном времени

Подготовьтесь к следующему собеседованию

Interview Boost учитывает вакансию, резюме и технологии и помогает сформулировать ответ прямо во время интервью.

Начать подготовку