TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.76K subscribers
Post #2189 2.41K
День 1811. #ЗаметкиНаПолях
Слабые События в C#

События являются распространённым источником утечек памяти, когда подписчик забывает отписаться от события, а жизненный цикл подписчика и издателя события разный. Можно попробовать использовать слабые события. Это события, которые не мешают сборщику мусора удалять подписчика.

Слабые события работают на основе WeakReference<T> — ссылки на объект, которая не предотвращает сбор мусора для объекта. В примере ниже мы храним как слабые ссылки на события. Также мы сохраняем делегаты обработчиков в привязке к объекту обработчика в ConditionalWeakTable. Это предотвращает преждевременное удаление делегатов сборщиком мусора.
class WeakEvent<TEventArgs>
{
private ImmutableList<
WeakReference<EventHandler<TEventArgs>>>
_listeners =
ImmutableList<
WeakReference<
EventHandler<TEventArgs>>>.Empty;

private readonly
ConditionalWeakTable<object, List<object>>
_delegates = [];

public void AddListener(
EventHandler<TEventArgs> handler)
{
if (handler == null)
return;

var weakRef = new
WeakReference<EventHandler<TEventArgs>>(handler);
_listeners = _listeners.Add(weakRef);
if (handler.Target != null)
_delegates
.GetOrCreateValue(handler.Target)
.Add(handler);
}

public void RemoveListener(
EventHandler<TEventArgs> handler)
{
if (handler == null)
return;

_listeners = _listeners
.RemoveAll(wr =>
!wr.TryGetTarget(out var target)
|| handler.Equals(target));
if (handler.Target != null
&& _delegates.TryGetValue(
handler.Target, out var weakRef))
{
weakRef.Remove(handler);
}
}

public void Raise(object? sender, TEventArgs args)
{
foreach (var l in _listeners)
{
if (l.TryGetTarget(out var target))
target.Invoke(sender, args);
else
{
// Удаляем делегат, если обработчик был удалён
_listeners = _listeners.Remove(l);
}
}
}
}


Вот пример, как использовать класс WeakEvent<TEventArgs>:
var sample = new Sample();
sample.CustomEvent +=
(sender, e) => Console.WriteLine("handler");
sample.DoAction();

class Sample
{
private readonly WeakEvent<EventArgs>
_event = new();

public event EventHandler<EventArgs> CustomEvent
{
add =>
_event.AddListener(value);
remove =>
_event.RemoveListener(value);
}

public void DoAction()
{
_event.Raise(this, EventArgs.Empty);
}
}


Источник: https://www.meziantou.net/weak-events-in-csharp.htm
  • 👍 9
More from @netdeveloperdiary
  1. Oct 8, 2026День 2808. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Начало В ИТ есть се…
  2. Oct 7, 2026День 2807. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Окончание Нач…
  3. Oct 6, 2026🦈 Открытое собеседование на Middle C# | 6 октября, 19:00 МСК Приглашаем на открытое собес…
  4. Oct 6, 2026День 2806. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Начало Больши…
  5. Oct 5, 2026День 2805. #ЧтоНовенького #NET11 Аргументы в Выражениях Коллекций в C#15 В C#15 реализован…
  6. Oct 4, 2026День 2804. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →