Дневник сертифицированного .NET разработчика. Заметки, советы, новости из мира .NET и C#.
Для связи: @SBenzenko
Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Post #2189
2.41K
День 1811. #ЗаметкиНаПолях
Слабые События в C#
События являются распространённым источником утечек памяти, когда подписчик забывает отписаться от события, а жизненный цикл подписчика и издателя события разный. Можно попробовать использовать слабые события. Это события, которые не мешают сборщику мусора удалять подписчика.
Слабые события работают на основе WeakReference<T> — ссылки на объект, которая не предотвращает сбор мусора для объекта. В примере ниже мы храним как слабые ссылки на события. Также мы сохраняем делегаты обработчиков в привязке к объекту обработчика в ConditionalWeakTable. Это предотвращает преждевременное удаление делегатов сборщиком мусора.
Вот пример, как использовать класс WeakEvent<TEventArgs>:
Источник: https://www.meziantou.net/weak-events-in-csharp.htm
Слабые События в 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


