В C# события (event) представляют собой специальный тип делегата, который позволяет нескольким методам подписаться на определенное событие и выполняться, когда это событие возникает. События используются для реализации шаблона "издатель-подписчик" и обеспечивают слабую связность между объектами.
События являются важной частью C# и позволяют создавать гибкие, модульные и масштабируемые приложения. Они широко используются во многих стандартных компонентах .NET, таких как кнопки, таймеры и другие элементы управления.
**Защита от Прямого Вызова**
События в C# защищены от прямого вызова извне объявленного класса или структуры, это служит для обеспечения инкапсуляции и безопасности. Вы можете вызвать событие только внутри класса или структуры, где оно было объявлено. Если вам нужно вызвать событие извне, вы должны предоставить метод или свойство, которое будет делать это внутри класса или структуры.
Пример
```csharp
using System;
public class MyEventPublisher
{
// Объявление события
public event Action MyEvent;
// Метод для внутреннего вызова события
public void RaiseEvent()
{
MyEvent?.Invoke();
}
}
class Program
{
static void Main(string[] args)
{
MyEventPublisher publisher = new MyEventPublisher();
// Попытка вызвать событие напрямую приведет к ошибке компиляции
publisher.MyEvent?.Invoke(); // Ошибка: событие "MyEvent" можно вызвать только внутри класса "MyEventPublisher".
}
}
```
##### Как использовать события
Определение и использование событий в C# происходит в несколько этапов:
1. Определение делегата: Делегат определяет сигнатуру методов, которые могут быть связаны с событием.
```csharp
public delegate void MyEventHandler(int someValue);
```
<br>
2. Определение события: Затем событие определяется на основе делегата. Обычно событие определяется внутри класса, который возбуждает событие (издатель).
```csharp
public class Publisher
{
public event MyEventHandler MyEvent;
// ...
}
```
Примечение: Не обязательно определять свой собственный делегат для использования событий в C#. Обычно, для большинства событий достаточно использовать общие делегаты, такие как Action, Func, EventHandler, которые уже определены в .NET Framework.
Как можно определить event c Action:
```csharp
public event Action ValueChanged;
```
<br>
3. Создание методов-обработчиков: Методы-обработчики должны соответствовать сигнатуре делегата и будут вызываться при возникновении события.
```csharp
public class Subscriber
{
public void OnMyEvent(int someValue)
{
// Обработка события здесь
}
}
```
<br>
4. Подписка на событие: Объекты-подписчики могут подписаться на событие, связав методы-обработчики с определенным событием.
```csharp
var publisher = new Publisher();
var subscriber = new Subscriber();
publisher.MyEvent += subscriber.OnMyEvent;
```
<br>
5. Возбуждение события: Когда событие возникает в издателе, все методы-обработчики, подписанные на это событие, вызываются автоматически.
```csharp
public class Publisher
{
public event MyEventHandler MyEvent;
public void TriggerEvent(int someValue)
{
MyEvent?.Invoke(someValue);
}
}
```
### Как сбросить все подписки
Просто установить его значение в null
```csharp
public class MyEventPublisher
{
public event EventHandler MyEvent;
public void ResetAllSubscriptions()
{
MyEvent = null; // Сбросить все подписки на событие
}
}
```
### Как отписать конкретный метод
Для отписки конкретного метода от события в C# используется оператор -=. Вам нужно использовать тот же метод и объект, на который вы подписывались, чтобы отписаться.
```csharp
// Отписываемся от события
publisher.MyEvent -= subscriber.MyHandler;
```
### Аллокация в куче
Операция подписки на событие `publisher.MyEvent += subscriber.OnMyEvent;` может привести к аллокации в куче (heap allocation).
Когда вы подписываетесь на событие с помощью метода экземпляра, создается делегат, который хранит ссылку на метод и ссылку на объект, чей метод вы подписываете. Этот делегат хранится в куче.
В современных версиях C# (начиная с C# 7.3) компилятор может выполнить оптимизации для аллокации делегатов в некоторых случаях, но в общем случае лучше считать, что операция подписки на событие с использованием метода экземпляра приведет к аллокации в куче.
Эта аллокация может не быть значительной проблемой, если подписка происходит один раз при инициализации и не повторяется часто в горячих путях кода. Однако, если это часть интенсивного цикла или часто вызываемого метода, это может привести к увеличению давления на сборщик мусора (Garbage Collector) и снижению производительности.
<br>
GPT-4