1. Введение
Вы могли этого не знать, но один из любимейших вопросов на собеседовании — “А что вы знаете о лямбда-выражениях?”. Почему про них спрашивают? Потому что это очень удобная и "модная" вещь в C#, позволяющая писать меньше кода и делать его выразительнее. Если анонимные методы — это как писать письмо от руки, то лямбда-выражения — как отправлять сообщение в мессенджере: быстро, компактно, и иногда даже со смайликами (ну, почти).
Лямбда-выражения пришли в C# с версии 3.0 и с тех пор распространились повсюду: в LINQ, обработчиках событий, потоках, коллекциях и даже библиотеках искусственного интеллекта на .NET.
Лямбда-выражение — это способ объявить метод прямо в теле кода, "на месте", без имени, суперкоротко и ясно. Основной синтаксис всегда выглядит так:
(параметры) => выражение_или_блок_кода
Этот "шеврон" => называют “лямбда-оператор” или “стрелка”.
Небольшая аналогия
Вспомните, как пишете рецепт: “Взять яблоко, порезать его, положить в миску”.
В C# это было бы:
(яблоко) => порезать(яблоко)
2. Переделываем анонимный метод в лямбду
Допустим, у нас есть делегат:
delegate int SquareDelegate(int x);
Анонимный метод выглядел бы так:
SquareDelegate sq = delegate(int x) {
return x * x;
};
А теперь то же самое через лямбда-выражение:
SquareDelegate sq = (int x) => { return x * x; };
Но C# умеет догадываться о типах, и мы можем еще сократить:
SquareDelegate sq = x => x * x;
Потрясающая компактность!
Сравнение с "обычным" методом и анонимной функцией
| Способ | Объявление | Где можно использовать | Минусы |
|---|---|---|---|
| Именованный метод | В классе | Везде | Нужно имя, больше кода |
| Анонимный метод | В коде | Только с делегатами | Более громоздкий синтаксис |
| Лямбда-выражение | В коде | Везде с делегатами | Иногда неочевиден тип |
3. Синтаксис лямбда-выражения: вариации
Параметры
Без параметров:
Action hello = () => Console.WriteLine("Привет, мир!");
Один параметр (можно не писать скобки):
Func<int, int> inc = x => x + 1;
Несколько параметров (нужны скобки):
Func<int, int, int> sum = (a, b) => a + b;
Тело лямбды
Одно выражение — без фигурных скобок и return:
x => x * x
Блок кода — фигурные скобки, нужен return:
(x, y) =>
{
int z = x + y;
return z * z;
}
Типы параметров
Чаще всего тип указывать не нужно — компилятор догадается. Но если хочется, пожалуйста:
(x, y) => x + y // Компилятор сам выводит типы.
(int x, int y) => x + y // Можно явно.
4. Нюансы и более реальные примеры
Использование с коллекциями
Помните наше учебное мини-приложение (например, список пользователей)? Допустим, у нас есть массив чисел:
int[] numbers = { 1, 2, 3, 4, 5 };
Нужно выбрать только четные числа:
var evenNumbers = numbers.Where(n => n % 2 == 0);
Здесь Where — метод расширения LINQ, а условие — наш лямбда-фильтр.
Передача в метод
Допустим, у нас есть метод-делегат:
public delegate bool Filter(int number);
public static int[] FilterNumbers(int[] data, Filter predicate)
{
var result = new List<int>();
foreach (var n in data)
if (predicate(n))
result.Add(n);
return result.ToArray();
}
Теперь передаем лямбда-выражение:
int[] evens = FilterNumbers(numbers, n => n % 2 == 0);
Лямбда в качестве обработчика событий
button.Click += (sender, args) => Console.WriteLine("Кнопка нажата!");
5. Лямбда-выражения и стандартные обобщенные делегаты
Лямбды тяжело представить без Func<>, Action<> и Predicate<>. Это специальные типы делегатов:
- Action — ничего не возвращает.
- Func — возвращает значение.
- Predicate — возвращает bool, обычно для фильтрации.
Пример с Action:
Action<string> log = message => Console.WriteLine(message);
log("Это сообщение через лямбду!");
Пример с Func:
Func<int, int, int> multiply = (a, b) => a * b;
int product = multiply(3, 5); // 15
Пример с Predicate:
Predicate<int> isNegative = n => n < 0;
bool test = isNegative(-7); // true
6. Лямбды с несколькими выражениями: когда нужен блок
Если лямбда должна выполнять несколько операций, используйте фигурные скобки и явный return:
Func<int, int, string> describeSum = (a, b) =>
{
int sum = a + b;
return $"Сумма: {sum}";
};
Без return компилятор возмутится (но не объяснит толком, почему — просто напишет, что "не все пути возвращают значение").
7. Возврат void: Action
Когда лямбда ничего не возвращает, используйте Action:
Action greet = () => Console.WriteLine("Улыбайтесь — код компилируется!");
Лямбда может содержать сколько угодно инструкций:
Action<int> printSquare = x =>
{
int sq = x * x;
Console.WriteLine($"Квадрат числа {x} равен {sq}");
};
8. Ограничения и типичные ошибки
Иногда хочется написать лямбду очень вольно, но компилятор не всегда согласен.
Нельзя объявлять переменную с тем же именем, что и захваченная снаружи.
Нужно следить за типами: если не совпадает сигнатура делегата и лямбды — будет ошибка.
Возврат значения обязателен, если делегат требует возвращаемого значения.
В сложных лямбдах не забывайте про фигурные скобки. Одно выражение — без скобок и return. Несколько операторов — скобки и return.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ