【问题标题】:Using WeakEventManager, hiding the actual event - revisited使用 Wea​​kEventManager,隐藏实际事件 - 重访
【发布时间】:2020-05-01 23:26:44
【问题描述】:

我有一个只能使用 WeakEventManager 订阅的事件(对于无法取消注册的 ViewModel,因为它们没有可靠的卸载/处置机制),我想隐藏原始事件以防止其他人使用 @ 987654323@.

一个问题是WeakEventManager 订阅的事件必须是公开的,即不隐藏。这篇文章 (Using WeakEventManager, hiding the actual event) 有一个有趣的解决方案,其中事件被包装在一个私有类中(带有一个公共事件)。

我采用了该解决方案并提出了以下帮助程序类:

public class WeakEvent<TEventArgs> where TEventArgs : EventArgs
{
    private class InternalWrapper
    {
        public event EventHandler<TEventArgs> InternalEvent;
        public void Invoke(object sender, TEventArgs args) => InternalEvent?.Invoke(sender, args);
    }

    private InternalWrapper _wrapper = new InternalWrapper();

    public void Subscribe(EventHandler<TEventArgs> handler)
    {
        WeakEventManager<InternalWrapper, TEventArgs>.AddHandler(_wrapper, nameof(InternalWrapper.InternalEvent), handler);
    }

    public void Invoke(object sender, TEventArgs args) => _wrapper.Invoke(sender, args);
}

我的问题是:事件可以被触发,但在InternalWrapper.Invoke 内,InternalEvent 总是有一个null 调用列表 - 好像WeakEventManager 创建的注册被垃圾收集了,虽然handler 是一个一个非常活跃的实例的方法。

可能是泛型吗?

【问题讨论】:

    标签: c# events memory-leaks garbage-collection


    【解决方案1】:

    答案是:不,不是泛型。

    只有在注册过程中指定的source与用于调用事件的相同时才会触发使用WeakEventManager注册的事件处理程序,即必须使用@987654324(从source)调用它@。

    这样做的一个后果是,发送者将始终是私有内部类的一个实例,对于任何消费代码实际上是无用的。

    对于我的最终实现,我在下面添加了一个重载Subscribe(Action&lt;TEventArgs&gt; action) 以保持使用代码的清洁,如果可能的话。 (如果需要,该方法返回 EventHandler&lt;TEventArgs&gt;,可用于调用 Unsubscribe。)

    编辑: 使用Action 重载时,如果未存储对返回的EventHandler 的引用,垃圾收集迟早会收集注册,并且操作将按预期触发。

    要考虑的另一件事是可以使用public void Invoke 从任何代码触发/引发事件,但无论如何我经常需要并实现这样的方法。

    我发现这段代码比“本机”WeakEventManager 实现更直接和易于使用 - 最重要的是,它通过使用 += 监听事件来防止消耗代码造成内存泄漏.

    public class WeakEvent<TEventArgs> where TEventArgs : EventArgs
    {
        private class WeakEventInternal
        {
            public event EventHandler<TEventArgs> InternalEvent;
            public void Invoke(TEventArgs args) => InternalEvent?.Invoke(this, args);
        }
    
        private readonly WeakEventInternal _internal= new WeakEventInternal();
    
        public void Subscribe(EventHandler<TEventArgs> handler) => WeakEventManager<WeakEventInternal, TEventArgs>.AddHandler(_internal, nameof(WeakEventInternal.InternalEvent), handler);
    
        public void Unsubscribe(EventHandler<TEventArgs> handler) => WeakEventManager<WeakEventInternal, TEventArgs>.RemoveHandler(_internal, nameof(WeakEventInternal.InternalEvent), handler);
    
        public EventHandler<TEventArgs> Subscribe(Action<TEventArgs> handler)
        {
            EventHandler<TEventArgs> internalHandler = (object s, TEventArgs ee) => handler(ee);
    
            WeakEventManager<WeakEventInternal, TEventArgs>.AddHandler(_internal, nameof(WeakEventInternal.InternalEvent), internalHandler);
    
            return internalHandler;
        }
    
        public void Invoke(TEventArgs args) => _internal.Invoke(args);
    }
    

    【讨论】:

    • 当然感谢 jeff (user:59546) 的原始实现!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-06
    • 2013-10-08
    • 1970-01-01
    相关资源
    最近更新 更多