在 C# 中,事件和事件处理程序历来是对象之间通信的核心。在用户界面基于 WinForms 和 WPF 构建的应用程序中尤其如此。但是,误用事件会出现一个常见问题——内存泄漏!当事件订阅者未正确取消订阅时,就会发生这些泄漏,从而导致保留引用,从而无法释放内存。在 C# 中输入弱事件!
我们将寻找一个有前途的解决方案来帮助应对这一挑战。弱事件允许垃圾回收器从订阅的对象中回收内存,而无需显式取消订阅操作,并有助于降低内存泄漏的风险。虽然我仍然鼓励您专注于应用程序中对象的适当生存期管理(毕竟这只是良好的实践和设计),但 C# 中的弱事件可以在这里为我们提供一些额外的保护!
事件处理中的内存泄漏主要源于事件发布者和订阅者之间的持久引用。在典型方案中,发布者通过事件委托对订阅者具有强引用。如果订阅者在释放之前未能取消订阅,则 .NET 垃圾回收器无法回收订阅者的内存,因为发布者仍保留对它的引用。
这种情况在生存期较长的应用程序或动态创建和销毁许多对象的应用程序中尤其成问题。如果我们习惯于使用事件订阅创建对象,其中发布者和订阅者在应用程序的整个生命周期中都存在......没什么大不了的!如果我们动态地创建这些,并且生命周期管理很简单......也没什么大不了的!但是,在大型应用程序中,对象生存期管理可能会很快变得复杂。
C# 中的弱事件引入了一种机制,其中事件订阅不会阻止订阅者的垃圾回收,从而有效降低内存泄漏的风险。
C# 中的弱事件是一种设计模式,它允许对事件订阅者进行垃圾回收,从而防止内存泄漏。传统上,这是不允许发生的,因为事件订阅使对象保持活动状态并防止它被收集 - 即使似乎没有其他人意识到它!
C# 中的弱事件在事件发布者的生命周期比订阅服务器长的情况下特别有用。通过在 C# 中实现弱事件,可以帮助确保订阅者不会仅仅因为订阅了某个事件而不必要地保留在内存中。这有助于保持最佳的内存使用率和应用程序性能...因为没有人愿意处理内存泄漏。尤其是在一种管理语言中,我们经常感到不受此影响!
WeakReference<T>在 C# 中弱事件的实现中起着神奇的作用。它保存对对象(订阅者)的引用,而不会阻止该对象被垃圾回收。这意味着,如果没有对引用对象的强引用,垃圾回收器可以回收引用对象的内存。在弱事件的上下文中,允许对事件处理程序进行垃圾回收,这是防止具有复杂事件处理的应用程序中的内存泄漏的关键。
在 C# 中实现弱事件涉及创建一个类来管理事件订阅 ,确保事件处理程序不会阻止订阅者的垃圾回收。以下是分步指南:WeakReference
让我们看一下如何在 C# 中执行弱事件的示例实现:
public sealed class WeakEvent<TEventArgs>
where TEventArgs : EventArgs
{
private readonly List<WeakReference<EventHandler<TEventArgs>>> _handlers;
private readonly ConditionalWeakTable<object, List<object>> _keepAlive;
public WeakEvent()
{
_handlers = [];
_keepAlive = [];
}
public void Subscribe(EventHandler<TEventArgs>? handler)
{
if (handler == null)
{
return;
}
_handlers.Add(new(handler));
if (handler.Target != null)
{
var delegateList = _keepAlive.GetOrCreateValue(handler.Target);
delegateList.Add(handler);
}
}
public void Ubsubscribe(EventHandler<TEventArgs>? handler)
{
if (handler == null)
{
return;
}
_handlers.RemoveAll(wr =>
wr.TryGetTarget(out var existingHandler) &&
existingHandler == handler);
if (handler.Target != null &&
_keepAlive.TryGetValue(handler.Target, out var delegateList))
{
delegateList.Remove(handler);
}
}
public void Raise(object sender, TEventArgs e)
{
foreach (var weakReference in _handlers.ToList())
{
if (weakReference.TryGetTarget(out var handler))
{
handler(sender, e);
}
else
{
_handlers.Remove(weakReference);
}
}
}
}
上面的示例提供了将事件处理程序挂钩和取消挂钩到源弱事件的机制。我们还可以调用以触发事件,并支持使用我们的变量处理匿名委托。这有助于支持匿名代表在不应该收集垃圾时收集垃圾!
下面是一个示例,说明我们如何利用带有 and 的类:WeakEvent<T>EventSourceEventListener
var eventSource = new EventSource();
var listener = new EventListener();
eventSource.CustomEvent.Subscribe(listener.OnCustomEvent);
eventSource.TriggerEvent();
eventSource.CustomEvent.Unsubscribe(listener.OnCustomEvent);
Console.ReadLine();
public sealed class EventSource
{
private readonly WeakEvent<EventArgs> _customEvent;
public EventSource()
{
_customEvent = new WeakEvent<EventArgs>();
}
public void Subscribe(EventHandler<TEventArgs>? handler) =>
_customEvent.Subscribe(handler);
public void Unsubscribe(EventHandler<TEventArgs>? handler) =>
_customEvent.Unsubscribe(handler);
public void TriggerEvent() =>
_customEvent .Raise(this, EventArgs.Empty);
}
public sealed class EventListener
{
public void OnCustomEvent(object? sender, EventArgs e)
{
Console.WriteLine("Event received.");
}
}
现在我们已经了解了如何在 C# 中使用弱事件(以及原因),让我们讨论一些最佳实践:
C# 中的弱事件是有效管理事件处理程序内存的有用解决方案。在对象生存期不断变化的复杂或动态应用中尤其如此。通过允许对订阅者进行垃圾回收,弱事件可以防止潜在的内存泄漏,从而提高应用程序性能。正确使用它们,以及了解它们最适合的场景,可以帮助您避免事件出现内存问题!