1

当我做

WeakEventManager<SystemEvents, EventArgs>
    .AddHandler(null, nameof(SystemEvents.DisplaySettingsChanged), OnDisplaySettingsChanged);

OnDisplaySettingsChanged永远不会被调用。但是,如果我改为使用正常的事件订阅,SystemEvents.DisplaySettingsChanged += OnDisplaySettingsChanged一切正常。

这是怎么回事?

4

1 回答 1

2

原来是它的WeakEventManager错。当事件被触发时,这意味着source它将用于静态事件源(来自参考源null的代码摘录):

protected void DeliverEvent(object sender, EventArgs args)
{
    ListenerList list;
    object sourceKey = (sender != null) ? sender : StaticSource;
    ...

sender绝不是null为了SystemEvents。相反,它传递一个私有实例SystemEventsWeakEventManager然后假定它是另一个它以前不知道的实例并且不调用处理程序。

这是我想出的解决方法:

class EventProxy
{
    private readonly Action<EventHandler> _subscribe;
    private readonly Action<EventHandler> _unsubscribe;

    public EventProxy(Action<EventHandler> subscribe, Action<EventHandler> unsubscribe)
    {
        _subscribe = subscribe;
        _unsubscribe = unsubscribe;
    }

    private EventHandler _event;
    public event EventHandler Event
    {
        add
        {
            if (_event == null)
                _subscribe(OnEvent);
            _event += value;
        }
        remove
        {
            // ReSharper disable once DelegateSubtraction
            _event -= value;
            if (_event == null)
                _unsubscribe(OnEvent);
        }
    }

    private void OnEvent(object sender, EventArgs args)
    {
        _event?.Invoke(this, args);
    }
}

使用示例:

var proxy = new EventProxy(h => SystemEvents.DisplaySettingsChanged += h, h => SystemEvents.DisplaySettingsChanged -= h);
WeakEventManager<EventProxy, EventArgs>.AddHandler(proxy, nameof(EventProxy.Event), OnDisplaySettingsChanged);

一些解释:

  • SystemEvents持有对 的强引用EventProxy,它持有对处理程序的弱引用(通过WeakEventManager
  • WeakEventManager订阅里面的事件时AddHandler,代理订阅原始事件
  • EventProxy充当静态事件和处理程序之间的代理,在原始事件触发时调用处理程序
  • 处理程序被收集后,WeakEventManager最终将运行清理,发现处理程序已死亡并取消订阅
  • 这将导致代理取消订阅原始事件,并最终被 GC 收集
于 2018-05-08T04:56:06.203 回答