Предупреждение: мой ответ на самом деле описывает захваченные переменные, которые отличаются от лямбда-лифтинга. Неправильно прочитанный вопрос (нужно спать). Но я потратил немного времени на написание этого, поэтому мне не хочется его удалять. Оставь это как сообщество WIKI.
Лямбда-лифтинг, часто называемый замыканиями, - это способ беспрепятственного разрешения доступа к переменным в области действия из вложенного лямбда-выражения.
Трудно вникнуть в мельчайшие детали замыканий, не выбирая определенный язык. Одним из побочных эффектов лямбда-лифтинга в любом языке является то, что он имеет тенденцию продлевать время жизни переменной от локальной короткоживущей области до намного более длительной области действия. Обычно это происходит в форме передачи переменной из стека в кучу внутри компилятора. Это очень специфичное для языка действие и, следовательно, производит очень разные реализации на основе языка.
Я сосредоточусь на C #, поскольку это, вероятно, язык, наиболее распространенный для читателей переполнения стека. Начнем со следующего кода.
public Func<int> GetAFunction() {
var x = 42;
Func<int> lambda1 = () => x;
Func<int> lambda2 = () => 42;
...
return lambda1;
}
В этом примере мы создали 2 лямбда-выражения. В обоих случаях он присваивается экземпляру делегата типа Func. Все делегаты в .Net требуют, чтобы реальная функция поддерживала их где-то. Таким образом, под капотом все лямбда-выражения / анонимные функции в C # переводятся в определение метода.
Создать функцию для lambda2 довольно просто. Это изолированная функция, которая просто возвращает постоянное значение.
public static int RealLambda2() {
return 42;
}
Генерировать lambda1 немного сложнее. Литеральное определение будет выглядеть следующим образом
public static int RealLambda1() {
return x;
}
Это, очевидно, не скомпилируется, потому что x не доступен. Для этого компилятор C # должен поднять переменную x в замыкание. Затем он может вернуть указатель на функцию в замыкании, чтобы удовлетворить выражение делегата
class Closure1 {
int x;
public int RealLambda1() {
return x;
}
}
Это довольно простой пример, но, надо надеяться, должен детализировать искусство подъема. К сожалению, дьявол кроется в деталях и все усложняет сценарий.