JavaRush /Курсы /C# SELF /Лямбда-выражения для event

Лямбда-выражения для event и callback

C# SELF
50 уровень , 3 лекция
Открыта

1. Введение

В C# (и в .NET вообще) события — один из способов, которыми объекты общаются между собой. Например, у вас есть кнопка, нажатие на которую должно что-то запускать. Можно было бы реализовать обработку событий через объявление отдельного метода, затем этот метод явно подписывать на событие... Но часто хочется просто “описать реакцию прямо здесь”, не размазывая логику по разным частям файла.

Лямбда-выражения прекрасно решают эту задачу — позволяют буквально написать реакцию на событие в том же месте, где подписываешься на это событие. Минимум церемониала — максимум пользы.

То же самое касается обратных вызовов (callback): иногда вы передаёте в метод некоторую логику для выполнения “позже”, например, когда операция завершится, и снова — лямбда-выражения делают это просто и наглядно: вся нужная логика — тут же, на месте вызова.

События и коллбэки: коротко о главном и где тут нужны лямбды

Событие — это не что иное, как “вывеска” на объекте: “вот тут кто-то может подписаться и сделать что-то, когда что-то случится”.

Callback (обратный вызов) — это “я тебе сейчас даю функцию, а ты вызови её, когда будет нужно”. Очень похоже на “оставь заявку — мы перезвоним”.

Когда вы знакомились с делегатами и событиями раньше, вы уже видели синтаксис примерно такой:

button.Click += MyButtonClickHandler;

Где MyButtonClickHandler — специальный метод, который надо где-то объявить. Но когда логика простая, хочется обойтись парой строк, не плодя методы ради одного действия.

Здесь и появляются на сцене лямбды:

button.Click += (sender, args) => { Console.WriteLine("Кнопка нажата!"); };

Лаконично, понятно, прямо здесь!

2. Лямбды как обработчики событий: от простого к интересному

Простая форма: лампочкой моргнул — в консоль что-то вывели

Предположим, у нас есть класс Button, и мы хотим реагировать на его событие Click.

// Представим себе такую кнопку:
public class Button
{
    public event EventHandler? Click;

    public void SimulateClick() // Для примера: "нажимаем" программно
    {
        // Если кто-то подписался — вызываем обработчики
        Click?.Invoke(this, EventArgs.Empty);
    }
}

Теперь используем эту кнопку:

var button = new Button();

// Подписываемся на событие кнопки с помощью лямбды!
button.Click += (sender, args) =>
{
    Console.WriteLine("Ура! Кнопку нажали!");
};

button.SimulateClick(); // => Ура! Кнопку нажали!

Пояснение:
— Всё, что после => — тело вашей “мини-функции”, которая будет вызвана, когда событие произойдёт.
— Не нужно думать о жизненном цикле обработчика: он живёт столько, сколько живёт ваша подписка (+=). Важно: если лямбда захватывает переменные извне и вы не отписываетесь от события, это может привести к утечкам памяти.

Лямбда-обработчик с захватом переменных

int clickCount = 0;

button.Click += (sender, args) =>
{
    clickCount++;
    Console.WriteLine($"Кнопка нажата уже {clickCount} раз(а)!");
};

button.SimulateClick(); // => Кнопка нажата уже 1 раз(а)!
button.SimulateClick(); // => Кнопка нажата уже 2 раз(а)!

Что происходит:
Лямбда "захватывает" переменную clickCount извне и помнит её значение между вызовами.

3. Применение лямбда-выражений для callbacks

Обратный вызов часто используется в ситуациях, когда некоторому коду нужно вызвать вашу функцию позже — например, после выполнения какой-то долгой работы.

Передача лямбды как callback

Допустим, у нас есть метод, который принимает делегат или лямбду в качестве аргумента:

void DoWork(Action callback)
{
    Console.WriteLine("Начинается работа...");
    // Имитация работы
    System.Threading.Thread.Sleep(500); // (Так делать не надо в реальных приложениях, это блокирующая операция!) 
    callback(); // После работы вызываем callback
}

DoWork(() => Console.WriteLine("Работа завершена!"));

Результат:
Сначала в консоли появится "Начинается работа...", затем "Работа завершена!".

Можно передавать лямбды с параметрами:

void Calculate(int a, int b, Action<int> onResult)
{
    int sum = a + b;
    onResult(sum);
}

Calculate(5, 8, result => Console.WriteLine($"Результат: {result}"));

4. Типичные ошибки при использовании лямбд

Неотписка от событий

Когда вы подписываетесь на событие через лямбду, у вас нет “имени обработчика”, чтобы потом легко отписаться:

button.Click += (s, e) => Console.WriteLine("...");
button.Click -= ??? // Как сюда передать вашу лямбду? Никак, если она не сохранена.

Если очень нужно отписываться — сохраните лямбду в переменную:

EventHandler handler = (s, e) => Console.WriteLine("...");
button.Click += handler;
// ...затем
button.Click -= handler;

Это важно для долгоживущих объектов: если не отписаться, ссылка останется и, возможно, приведёт к утечке памяти.

Захват переменных цикла

Классика: подписка на события внутри цикла:

for (int i = 0; i < 3; i++)
    button.Click += (s, e) => Console.WriteLine(i); // Все лямбды "помнят" один и тот же i

В современных версиях C# это поведение исправлено для foreach, но для for иногда могут быть нюансы — всегда будьте внимательнее с захваченными переменными!

Если нужно, чтобы каждая лямбда помнила своё значение — создаём локальную копию:

for (int i = 0; i < 3; i++)
{
    int localI = i;
    button.Click += (s, e) => Console.WriteLine(localI);
}
2
Задача
C# SELF, 50 уровень, 3 лекция
Недоступна
Счётчик нажатий с захватом переменных
Счётчик нажатий с захватом переменных
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Slevin Уровень 57
3 марта 2026
Замечу: информация про события и что это такое, будет ЧЕРЕЗ 2 УРОВНЯ. Что ж, вернемся попозже. Спасибо JavaRush... 👎