Ответ
По умолчанию событие в C# — это обёртка над multicast-делегатом. При вызове события все подписанные обработчики вызываются последовательно в порядке их добавления через оператор +=. Если у события нет подписчиков, его вызов через ?.Invoke() безопасно возвращает null и ничего не происходит.
Ключевые особенности:
- Порядок вызова: Обработчики вызываются в порядке подписки (FIFO).
- Исключения: Если один обработчик выбрасывает исключение, последующие обработчики в цепочке не выполняются. Это критично для надёжных систем.
- Потокобезопасность: Операции
+=и-=являются атомарными (потокобезопасными), но конкурентная подписка и вызов события требуют дополнительной синхронизации. - Утечки памяти: Классическая проблема — удержание в памяти объектов-подписчиков. Необходима явная отписка (
-=) перед удалением подписчика.
Пример стандартного использования:
public class Publisher
{
public event EventHandler<EventArgs> OnDataProcessed;
public void ProcessData()
{
// Логика обработки...
OnDataProcessed?.Invoke(this, EventArgs.Empty); // Вызов всех подписчиков
}
}
public class Subscriber
{
public void Subscribe(Publisher pub)
{
pub.OnDataProcessed += HandleNotification;
}
private void HandleNotification(object sender, EventArgs e)
{
Console.WriteLine("Данные обработаны.");
}
}
Кастомное поведение (add/remove аксессоры): Для реализации собственной логики подписки (например, логирования, гарантированного вызова всех обработчиков или weak references) можно переопределить аксессоры add и remove:
private EventHandler _customEvent;
public event EventHandler CustomEvent
{
add
{
Console.WriteLine($"Добавлен обработчик: {value.Method.Name}");
_customEvent += value;
}
remove
{
Console.WriteLine($"Удалён обработчик: {value.Method.Name}");
_customEvent -= value;
}
} Ответ 18+ 🔞
А, ну вот, опять эти события в C#. Слушай, как будто в первый раз, блядь. По умолчанию — это просто такая хитрая обёртка над делегатом, который может много подписчиков держать. Называется multicast-делегат, ну, чтоб ты знал. Вызвал событие — и понеслась, все обработчики, которые на него подписались, начинают по очереди отрабатывать, как в очереди за водкой. Кто первый пришёл, того и тапки.
Что важно, а то потом будешь головой об стенку биться:
- Порядок вызова: Как подписались через
+=, в таком порядке их и вызовут. Первый пришёл — первый ушёл, всё честно. - Исключения: Вот тут, блядь, самое интересное. Если один из этих обработчиков возьмёт и выкинет исключение — всё, пиздец, остальные просто не отработают. Цепочка рвётся, как гнилая нитка. Для серьёзных систем это вообще жесть, надо всегда помнить.
- Потокобезопасность: Операции
+=и-=сами по себе атомарные, то есть потокобезопасные. Но если у тебя одновременно десять потоков лезут подписываться и вызывать событие — тут уже надо головой думать и синхронизацию городить, иначе будет каша, хуле. - Утечки памяти: Классика жанра, ебать! Объект подписался и забыл отписаться. А издатель-то его держит крепко, как родного. И сидит твой объект в памяти, как призрак, пока сборщик мусора не заплачет. Всегда отписывайся через
-=, когда объект больше не нужен, это святое.
Ну, смотри, как обычно это выглядит на практике:
public class Publisher
{
public event EventHandler<EventArgs> OnDataProcessed;
public void ProcessData()
{
// Тут что-то делаем умное...
OnDataProcessed?.Invoke(this, EventArgs.Empty); // И бабахаем всем подписчикам!
}
}
public class Subscriber
{
public void Subscribe(Publisher pub)
{
pub.OnDataProcessed += HandleNotification;
}
private void HandleNotification(object sender, EventArgs e)
{
Console.WriteLine("Данные обработаны, ура.");
}
}
А если хочешь поумничать и своё поведение прикрутить? Ну, например, логировать, кто подписался, или сделать так, чтобы обработчики все до одного отработали, даже если один сдох. Для этого есть кастомные аксессоры add и remove. Выглядит, конечно, как шаманство, но работает:
private EventHandler _customEvent;
public event EventHandler CustomEvent
{
add
{
Console.WriteLine($"Добавлен обработчик: {value.Method.Name}");
_customEvent += value;
}
remove
{
Console.WriteLine($"Удалён обработчик: {value.Method.Name}");
_customEvent -= value;
}
}
Вот и вся магия. Главное — не накосячить с исключениями и не породить утечек, а то потом будешь искать, куда память девается, ебать колотить.