Шаблоны событий и подводные камни (утечки памяти)
Находите и исправляйте проблемы с событиями: отписывайтесь, чтобы избежать утечек памяти, сохраняйте ссылки на делегаты для их удаления и используйте шаблон подписки IDisposable.
«Шаблоны событий и подводные камни (утечки памяти)» — бесплатный урок C# Academy на CoddyKit. Это урок 3 из 3. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 3 уроков всего.
Обзор подводных камней
Цель: безопасно использовать события.
- Отменяйте подписку, чтобы предотвращать утечки памяти
- Храните ссылку на того же обработчика, чтобы удалить его
- Отдавайте предпочтение небольшому шаблону IDisposable для очистки
- Избегайте встроенных анонимных обработчиков, которые невозможно отключить
Отмена подписки для предотвращения утечек
События хранят ссылки на подписчиков. Если издатель существует долго и Вы никогда не отключаете подписчика, сборщик мусора не сможет его удалить.
using System;
public class Publisher
{
public event EventHandler Tick;
public void Fire()
{
EventHandler h = Tick;
if (h != null) h(this, EventArgs.Empty);
}
}
public class Subscriber
{
public void Handle(object s, EventArgs e)
{
Console.WriteLine("Handling tick");
}
public void Subscribe(Publisher p) { p.Tick += Handle; }
public void Unsubscribe(Publisher p) { p.Tick -= Handle; } // required to avoid leaks
}
public class Program
{
public static void Main(string[] args)
{
Publisher p = new Publisher();
Subscriber s = new Subscriber();
s.Subscribe(p);
p.Fire(); // prints once
s.Unsubscribe(p); // detach when done
s = null; // now eligible for GC in a real app
p.Fire(); // no output
}
}
Хранение ссылки на обработчик
Чтобы отключить анонимный обработчик, сохраните его в переменной и передайте тот же экземпляр оператору -=.
using System;
public class Button
{
public event EventHandler Click;
public void Simulate()
{
EventHandler h = Click;
if (h != null) h(this, EventArgs.Empty);
}
}
public class Program
{
public static void Main(string[] args)
{
Button b = new Button();
// BAD: cannot easily remove this later
// b.Click += (s, e) => Console.WriteLine("inline");
// GOOD: store the handler, attach, later detach the same instance
EventHandler saved = (s, e) => Console.WriteLine("saved");
b.Click += saved;
b.Simulate(); // prints "saved"
b.Click -= saved; // can remove because we kept the reference
b.Simulate(); // no output
}
}
Подписка с освобождением ресурсов
Возвращайте IDisposable из Subscribe. Используйте using, чтобы автоматически отменять подписку.
using System;
public class Ticker
{
public event EventHandler Tick;
public IDisposable Subscribe(EventHandler handler)
{
Tick += handler;
return new Unsubscriber(this, handler);
}
private sealed class Unsubscriber : IDisposable
{
private Ticker _t;
private EventHandler _h;
private bool _done;
public Unsubscriber(Ticker t, EventHandler h) { _t = t; _h = h; }
public void Dispose()
{
if (!_done)
{
_t.Tick -= _h;
_done = true;
}
}
}
public void Fire()
{
EventHandler h = Tick;
if (h != null) h(this, EventArgs.Empty);
}
}
public class Program
{
public static void Main(string[] args)
{
Ticker t = new Ticker();
using (t.Subscribe((s, e) => Console.WriteLine("tick")))
{
t.Fire(); // prints once
}
t.Fire(); // nothing after disposal
}
}
Отключение через try/finally
Используйте try/finally, если обработчик необходимо отключить даже в случае возникновения исключения.
using System;
public class Alarm
{
public event EventHandler Ring;
public void Trigger()
{
EventHandler h = Ring;
if (h != null) h(this, EventArgs.Empty);
}
}
public class Program
{
public static void Main(string[] args)
{
Alarm a = new Alarm();
EventHandler h = (s, e) => Console.WriteLine("ring");
a.Ring += h;
try
{
a.Trigger(); // do work with subscription active
}
finally
{
a.Ring -= h; // always detach
}
a.Trigger(); // no output
}
}
Советы по предотвращению утечек
Советы:
- Подписчики должны сами отвечать за отмену подписки (Dispose, finally или явный метод).
- Избегайте подписки с помощью встроенных анонимных обработчиков, которые невозможно удалить.
- Будьте осторожны со статическими событиями: они часто существуют дольше объектов.
- Делайте обработчики короткими; не блокируйте издателя.
Причина утечек при использовании событий
Повторение
Повторение: отменяйте подписку, чтобы предотвращать утечки, храните ссылки на обработчики для их удаления и отдавайте предпочтение шаблонам очистки с освобождением ресурсов или на основе finally, чтобы безопасно управлять временем жизни событий.
Часто задаваемые вопросы
Урок «Шаблоны событий и подводные камни (утечки памяти)» бесплатный?
Да — полный текст урока «Шаблоны событий и подводные камни (утечки памяти)» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 3 уроков всего.
Чему я научусь в уроке «Шаблоны событий и подводные камни (утечки памяти)»?
Находите и исправляйте проблемы с событиями: отписывайтесь, чтобы избежать утечек памяти, сохраняйте ссылки на делегаты для их удаления и используйте шаблон подписки IDisposable. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C# Academy?
Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 3.
Сколько времени занимает урок «Шаблоны событий и подводные камни (утечки памяти)»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C# Academy?
Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Делегаты, Action/Func и замыкания
- События: публикация и подписка, ключевое слово event
- Шаблоны событий и подводные камни (утечки памяти)