You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何引发弱事件?.NET弱事件管理器使用困惑及最简方案求助

关于WPF弱事件模式的困惑与最简实现方案

一、为什么现有弱事件管理器没有引发事件的接口?

PropertyChangedEventManager和泛型WeakEventManager<TEventSource, TEventArgs>的核心作用是管理事件订阅的弱引用关系,避免订阅者因持有事件源的强引用而无法被GC回收,从而防止内存泄漏。它们本身并不负责触发事件——事件的触发逻辑仍然由事件源类自行实现,和普通事件的触发方式完全一致。

这些类被列为弱事件模式的实现方式,是因为它们封装了弱事件模式最核心的“弱引用订阅”逻辑,你只需在订阅事件时通过它们添加处理程序,而非直接订阅事件源的普通事件,就能实现弱引用订阅的效果。

二、最简的弱事件引发与处理方案

方案1:使用泛型WeakEventManager管理自定义事件

无需继承WeakEventManager,直接用它封装自定义事件,让订阅者通过它完成订阅,事件源正常触发事件即可。

示例代码:

// 事件源类
public class CustomEventSource
{
    // 内部普通事件,用于触发逻辑
    private event EventHandler<CustomEventArgs>? InternalCustomEvent;

    // 对外提供弱引用订阅入口
    public void AddCustomEventHandler(EventHandler<CustomEventArgs> handler)
    {
        WeakEventManager<CustomEventSource, CustomEventArgs>.AddHandler(this, nameof(InternalCustomEvent), handler);
    }

    public void RemoveCustomEventHandler(EventHandler<CustomEventArgs> handler)
    {
        WeakEventManager<CustomEventSource, CustomEventArgs>.RemoveHandler(this, nameof(InternalCustomEvent), handler);
    }

    // 事件触发方法,由事件源自身调用
    public void RaiseCustomEvent(string message)
    {
        InternalCustomEvent?.Invoke(this, new CustomEventArgs(message));
    }
}

// 自定义事件参数
public class CustomEventArgs : EventArgs
{
    public string Message { get; }
    public CustomEventArgs(string message) => Message = message;
}

// 订阅者类
public class Subscriber
{
    public void OnCustomEvent(object? sender, CustomEventArgs e)
    {
        Console.WriteLine($"收到事件:{e.Message}");
    }
}

// 使用示例
var source = new CustomEventSource();
var subscriber = new Subscriber();

// 通过WeakEventManager完成弱引用订阅
source.AddCustomEventHandler(subscriber.OnCustomEvent);

// 触发事件
source.RaiseCustomEvent("测试弱事件");

// 订阅者置空后可被GC正常回收,不会因事件订阅被强引用滞留
subscriber = null;
GC.Collect();

方案2:自行实现极简弱事件(不依赖WPF类)

如果不想依赖WPF的WeakEventManager,可以用WeakReference手动实现轻量弱逻辑:

public class WeakEvent<TEventArgs> where TEventArgs : EventArgs
{
    private readonly List<WeakReference<EventHandler<TEventArgs>>> _handlers = new();

    public void AddHandler(EventHandler<TEventArgs> handler)
    {
        _handlers.Add(new WeakReference<EventHandler<TEventArgs>>(handler));
    }

    public void RemoveHandler(EventHandler<TEventArgs> handler)
    {
        // 清理已回收的引用并移除匹配的处理程序
        _handlers.RemoveAll(wr => !wr.TryGetTarget(out var target) || target == handler);
    }

    // 事件触发方法,由事件源调用
    public void Invoke(object sender, TEventArgs e)
    {
        var toRemove = new List<WeakReference<EventHandler<TEventArgs>>>();
        foreach (var wr in _handlers)
        {
            if (wr.TryGetTarget(out var handler))
            {
                handler.Invoke(sender, e);
            }
            else
            {
                toRemove.Add(wr);
            }
        }
        // 批量清理无效引用
        foreach (var wr in toRemove) _handlers.Remove(wr);
    }
}

// 事件源类
public class CustomEventSource
{
    private readonly WeakEvent<CustomEventArgs> _customEvent = new();

    // 对外暴露标准事件接口
    public event EventHandler<CustomEventArgs> CustomEvent
    {
        add => _customEvent.AddHandler(value);
        remove => _customEvent.RemoveHandler(value);
    }

    public void RaiseCustomEvent(string message)
    {
        _customEvent.Invoke(this, new CustomEventArgs(message));
    }
}

// 订阅者和使用方式与方案1一致

总结

  • 弱事件管理器仅负责维护弱引用订阅关系,事件触发是事件源的职责,这就是它们没有引发事件接口的原因。
  • 最简实现优先选择泛型WeakEventManager封装事件,无需继承,只需通过它的AddHandler/RemoveHandler实现弱引用订阅,事件源正常触发内部事件即可。

内容的提问来源于stack exchange,提问作者Hossein Ebrahimi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 01:11:32