Ответ
Лямбда-выражение — это компактный синтаксис для создания анонимного метода, который может быть неявно преобразован в экземпляр делегата или в дерево выражений (Expression<T>). Компилятор C# автоматически генерирует код делегата на основе лямбды.
Практический пример:
// Лямбда-выражение присваивается делегату типа Func<int, int, int>
Func<int, int, int> add = (x, y) => x + y;
int result = add(5, 3); // result = 8
// Та же лямбда может быть использована для другого совместимого делегата
Action<int, int> printSum = (x, y) => Console.WriteLine(x + y);
printSum(5, 3); // Вывод: 8
Ключевые моменты связи:
- Автоматическое преобразование: Компилятор выводит тип делегата из контекста. Сигнатура лямбды (типы параметров и возвращаемого значения) должна соответствовать сигнатуре делегата.
- Замыкания: Лямбда может захватывать ("видеть") локальные переменные и параметры метода, в котором она объявлена. Для этого компилятор создает скрытый класс-замыкание.
int multiplier = 2; Func<int, int> multiplierFunc = x => x * multiplier; // Захват переменной multiplier - Деревья выражений: Если лямбда присваивается типу
Expression<TDelegate>, компилятор строит дерево выражений (данные о коде), а не исполняемый IL-код. Это основа таких технологий, как LINQ to SQL.// Дерево выражений, а не делегат Expression<Func<int, int, int>> expression = (x, y) => x + y;Использование лямбд вместо явного создания делегатов через
new DelegateType()делает код значительно чище и читабельнее.
Ответ 18+ 🔞
А, лямбды! Ну это ж просто пиздец как удобно, если разобраться. По сути, это такой короткий способ написать анонимный метод, который компилятор сам, хитрая жопа, превратит либо в делегат, либо в дерево выражений — Expression<T>. Не надо городить огород с отдельными методами.
Смотри, как это на практике выглядит:
// Вот тебе лямбда, которую сразу суём в делегат Func<int, int, int>
Func<int, int, int> add = (x, y) => x + y;
int result = add(5, 3); // result = 8, ебать колотить!
// А эту же самую хрень можно и в другой делегат запихнуть, если сигнатура сходится
Action<int, int> printSum = (x, y) => Console.WriteLine(x + y);
printSum(5, 3); // Напечатает: 8
А теперь главные фишки, чтобы не облажаться:
- Компилятор — не дурак. Он сам сообразит, во что превращать лямбду, глядя на контекст. Главное, чтобы параметры и возвращаемый тип у тебя в лямбде совпадали с тем, что делегат ожидает. Не совпадут — получишь по ебалу ошибкой.
- Замыкания — вот где магия, блядь. Лямбда может хавать переменные из того метода, где её объявили. Компилятор для этого невидимый класс создаёт, чтобы эти переменные не потерялись. Смотри:
int multiplier = 2; Func<int, int> multiplierFunc = x => x * multiplier; // Захватил переменную multiplier, и всё работает - Деревья выражений — это уже для продвинутых. Если ты лямбду суёшь не в обычный делегат, а в
Expression<TDelegate>, то компилятор не код генерит, а строит дерево выражений — структуру данных, описывающую твою операцию. Это основа всяких LINQ провайдеров, которые в базу запросы переводят.// Это уже не исполняемый код, а его описание Expression<Func<int, int, int>> expression = (x, y) => x + y;
В общем, если сравнивать с тем, чтобы вручную через new DelegateType() делегаты создавать, то лямбды — это просто небо и земля. Код чище, понятнее, и писать в разы быстрее. Ёпта, красота же!