0Pricing
C# Academy · Урок

Шаблоны событий и подводные камни (утечки памяти)

Находите и исправляйте проблемы с событиями: отписывайтесь, чтобы избежать утечек памяти, сохраняйте ссылки на делегаты для их удаления и используйте шаблон подписки 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 или явный метод).
  • Избегайте подписки с помощью встроенных анонимных обработчиков, которые невозможно удалить.
  • Будьте осторожны со статическими событиями: они часто существуют дольше объектов.
  • Делайте обработчики короткими; не блокируйте издателя.

Причина утечек при использовании событий

Быстрая проверка: что часто вызывает утечки памяти при использовании событий C#?

Повторение

Повторение: отменяйте подписку, чтобы предотвращать утечки, храните ссылки на обработчики для их удаления и отдавайте предпочтение шаблонам очистки с освобождением ресурсов или на основе 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 — локальная установка не требуется.

Все уроки этого курса

  1. Делегаты, Action/Func и замыкания
  2. События: публикация и подписка, ключевое слово event
  3. Шаблоны событий и подводные камни (утечки памяти)
← Назад к C# Academy