Я чувствую, что у меня есть довольно достойное понимание закрытий, как использовать их, и когда они могут быть полезными. Но то, что я не понимаю, - то, как они на самом деле выполняют незаметную работу в памяти. Некоторый пример кода:
public Action Counter()
{
int count = 0;
Action counter = () =>
{
count++;
};
return counter;
}
Обычно, если бы {количество} не было получено закрытием, то его жизненный цикл был бы ограничен по объему к Счетчику () метод, и после того, как это завершается, это ушло бы с остальной частью выделения стека для Счетчика (). Что происходит хотя, когда это закрывается? Целое складывает выделение для этого вызова Счетчика (), слоняются поблизости? Это копирует {рассчитывают} к "куче"? Это на самом деле никогда не становится выделенным на стеке, но распознанным компилятором, как закрываемым, и поэтому всегда живет на "куче"?
Для этого конкретного вопроса я, прежде всего, интересуюсь тем, как это работает в C#, но не было бы настроено против сравнений с другими языками та поддержка закрытия.
Компилятор (в отличие от среды выполнения) создает другой класс / тип. Функция с вашим закрытием и любые переменные, которые вы закрыли / подняли / захватили, переписываются во всем вашем коде как члены этого класса. Замыкание в .Net реализовано как один экземпляр этого скрытого класса.
Это означает, что ваша переменная count полностью является членом другого класса, и время жизни этого класса работает как любой другой объект clr; он не подлежит сборке мусора, пока не перестанет быть внедренным. Это означает, что пока у вас есть вызываемая ссылка на метод, он никуда не денется.
Ваша третья догадка верна. Компилятор сгенерирует такой код:
private class Locals
{
public int count;
public void Anonymous()
{
this.count++;
}
}
public Action Counter()
{
Locals locals = new Locals();
locals.count = 0;
Action counter = new Action(locals.Anonymous);
return counter;
}
Имеет смысл?
Кроме того, вы просили сравнения. VB и JScript создают замыкания практически одинаково.