Что такое замыкание?
Что проверяют
Понимаете ли вы, что лямбда в C# захватывает переменную по ссылке через сгенерированный компилятором класс окружения, и какие из этого следуют эффекты: общее состояние, продление жизни объектов и лишние аллокации.
Как отвечать
Замыкание это лямбда или анонимный метод вместе с захваченными локальными переменными объемлющего метода. Компилятор переносит захваченные переменные в поля скрытого класса окружения, а тело лямбды становится методом этого класса. Отсюда главное свойство: захват идёт по ссылке, а не копией значения.
int counter = 0;
Func<int> next = () => ++counter; // counter стал полем класса окружения
next(); // 1
counter += 10; // видно изнутри лямбды
next(); // 12Если лямбда трогает поле или метод экземпляра, она захватывает сам экземпляр, и он живёт, пока жив делегат. Когда захват не нужен, лямбду помечают как static: компилятор запретит случайный захват, а делегат можно переиспользовать.
Где спотыкаются
Захватывают переменную цикла. В обычном for переменная одна на весь цикл, поэтому все созданные в теле лямбды увидят конечное значение; в современных версиях языка переменная foreach создаётся заново на каждой итерации, и там проблемы нет. Надёжный приём это локальная копия внутри тела цикла.
Считают захват копированием. Тогда становится сюрпризом, что изменение переменной после создания делегата видно внутри, а два делегата из одного метода работают с общим состоянием, а не каждый со своим.
Не думают о времени жизни и аллокациях. Лямбда, подписанная на долгоживущее событие и захватившая экземпляр, держит его до отписки, и это типовая утечка. Плюс объект окружения создаётся при каждом вызове метода, а все лямбды одной области делят один такой объект, поэтому лёгкий колбэк может продлевать жизнь тяжёлым данным, захваченным соседней лямбдой.