Подписался на событие через += , но обработчик не вызывается — что не так?
Делаю простую кнопку-эмулятор и хочу через событие (event) уведомлять подписчиков о клике. Объявил событие, подписался через +=, но когда вызываю — ничего не происходит, обработчик молчит. Уже час смотрю и не вижу ошибку.
class Button
{
public event Action Clicked;
public void Click()
{
Clicked(); // тут падает или ничего
}
}
class Program
{
static void Main()
{
var btn = new Button();
// забыл подписаться? или нет?
btn.Click();
}
}
Иногда вообще ловлю NullReferenceException на строке Clicked(). Что я делаю не так и как событие вообще связано с делегатами?
2 ответа
Тут две вещи. Во-первых, в Main ты не подписался на событие — поэтому ничего и не вызывается. Во-вторых, если подписчиков нет, поле события равно null, и вызов Clicked() падает с NullReferenceException.
Событие — это, по сути, обёртка над делегатом (Action — это делегат). Снаружи на него можно только подписаться (+=) и отписаться (-=), а вызывать его может только сам класс. Поэтому перед вызовом всегда проверяй на null. Удобнее всего через ?.Invoke:
class Button
{
public event Action Clicked;
public void Click()
{
Clicked?.Invoke(); // вызовется, только если есть подписчики
}
}
static void Main()
{
var btn = new Button();
btn.Clicked += () => Console.WriteLine("Кнопка нажата!");
btn.Click(); // теперь работает
}
Ключевое: подписка через += и проверка ?.Invoke() решают обе проблемы сразу.
Дополню про связь события и делегата, раз ты спросил. event — это не отдельная сущность, а модификатор поверх делегата. Запись public event Action Clicked; означает «поле-делегат Action, но снаружи доступны только += и -=».
Если убрать event, то снаружи можно было бы написать btn.Clicked = null или даже btn.Clicked() — то есть любой смог бы затереть подписчиков или дёрнуть событие. event это запрещает и оставляет управление только внутри класса. Поэтому события и делают через event, а не через публичный делегат.
Для «настоящих» событий по конвенции используют EventHandler / EventHandler<T>, но Action для учебных примеров вполне подходит.