【问题标题】:How to create a Weak Referenced Event Handler?如何创建弱引用事件处理程序?
【发布时间】:2015-04-08 09:40:12
【问题描述】:

我正在研究如何正确创建弱引用事件处理程序。由于 WPF 已经有了避免事件内存泄漏的解决方案,所以我反编译了“WeakEventManager”类并花了一些时间分析它。

我发现,“WeakEventManager”类依赖于创建和存储对目标对象以及事件处理程序委托的弱引用。以下是一些代码段:

this._list.Add(new WeakEventManager.Listener(target, handler));


  public Listener(object target, Delegate handler)
  {
       this._target = new WeakReference(target);
       this._handler = new WeakReference((object) handler);
  }

我问自己这个简单的解决方案是否已经有效,或者我是否忽略了一个重要方面,因为我在 Internet 上找到的大多数其他解决方案都很复杂且难以理解。到目前为止我能找到的最佳解决方案 使用未绑定的委托。这是某种将事件处理程序和事件订阅者实例作为参数的包装委托(是的,它需要在委托调用期间传入事件订阅者对象)。

你可以在这里找到这篇很棒的文章:

http://diditwith.net/CommentView,guid,aacdb8ae-7baa-4423-a953-c18c1c7940ab.aspx#commentstart

“WeakEventManager”类不依赖于知道订阅者类或任何其他信息。除此之外,它还适用于匿名代表。如果一个解决方案只需要他们存储对委托的弱引用,为什么开发人员会花这么多时间来编写一个不仅可以工作而且使用方便的解决方案?有什么收获?

更新:因为有人对这个问题投了反对票(可能有点不具体),所以我想提出一个更准确的问题:

该源代码是否首先需要创建一个有效的弱事件处理程序?如果没有,缺少什么?

【问题讨论】:

  • 您不需要反编译框架 .net 程序集,Microsoft provides the source code。这样你也可以在他们提供的代码中看到 cmets。
  • 是的,你是对的。不幸的是,这次我无法使用“ProtectedAddHandler”方法。当我尝试时,我总是被重定向到页面顶部^^
  • 它在line 253
  • “它也适用于匿名代表”——这是一个毫无意义的陈述。一个委托是“匿名的”,因为它引用了一个匿名的方法;委托实例本身与任何其他委托完全一样,并且不需要额外的努力来支持此类委托。实际上,即使是匿名方法也只是声明它的匿名方法;编译器仍然给它一个名字。
  • 不,这不是毫无意义的。由于基于未绑定委托的解决方案需要事件订阅者实例作为参数才能正常工作,因此您不能简单地声明匿名委托,因为它可能与实例无关。这是大多数非 WPF 解决方案的一大缺点。

标签: c# wpf weak-references


【解决方案1】:

我是否忽略了一个重要方面

是的,一个重要的。你用一个“泄漏”换了另一个。现在,您不再阻止事件订阅者对象被垃圾回收,而是阻止 WeakReference 对象被回收。你没有领先。

需要一个机制来清理那些陈旧的弱引用。当它们的 IsAlive 属性返回 false 时,您不再需要它们,然后将其从 _list 中删除。但是您必须单独检查,一些 代码需要处理这一点。一个明显的选择是检查事件何时被添加或何时被触发。但这还不够,因为客户端代码只会在初始化时添加,并且您无法保证始终触发事件。

所以以后需要一些种方案来做。一切皆有可能,没有什么是非常理想的,因为它是很多忙碌的工作,经常没有完成任何事情。做出正确的选择很重要。这当然包括这样做,它往往是解决设计问题的创可贴。

【讨论】:

  • 你是对的。我没有提到这一点是为了清楚地了解具有所有魔力的“丰满代码”。 WeakEventManager 类包含一个用于此目的的 Purge 方法,该方法由任何事件源对象触发。
【解决方案2】:

WeakEventManager 背后的想法是,它保留对事件目标对象的弱引用列表和必须调用的处理程序列表,但不会“将它们绑定在一起”(通常直接订阅事件确实),允许目标被垃圾收集(并定期检查,以便在源被垃圾收集时相应地清空“订阅”委托列表)。

这听起来很简单,但需要大量的管道代码,这就是 WeakEventManager 所做的一切(以易于使用的方式为您完成管道)。

如果您将WeakEventManager 克隆到您自己的实现(或使用内置)并且它做到了,那么是的,这就是您需要做的所有事情。如果这不是您要问的,那么我不清楚这个问题。

【讨论】:

  • 虽然 WeakEventManager 确实比填充所需的逻辑更多,但我对负责使引用变弱的特定代码感兴趣。当您只关注该特定代码时,实际上似乎是两行代码(由此可以避免目标对象的弱引用,因为处理程序引用了目标对象)。我很惊讶,因为我不敢相信事情就这么简单。
  • 我刚才看到了这个,所以我可能错了,但我认为WeakEventManager 在内部使用ConditionalWeakTable 来存储“订阅”的代表(或代表列表)。通过这种方式,它可以将委托与目标对象相关联,而无需保持对它们的强引用,从而允许目标对象被 garbaje 收集。
  • 它实际上并没有做更多的事情(它以线程安全的方式安排清理并调用委托,但据我所知,仅此而已)。这就是我所说的“管道”:-)
  • CWT 在创建弱引用中扮演什么角色?在 .NET 源代码中,处理程序被添加了两次。到普通列表以及 CWT。
  • 密钥存储很弱,因此您将目标对象存储为密钥,并允许对其进行垃圾回收。它在我链接的文档中
【解决方案3】:

该源代码是否首先需要创建一个有效的弱事件处理程序?如果没有,缺少什么?

这是最重要的一点,Listener 类的其余部分也很重要,更不用说支持此非委托事件处理程序所需的手动管道。

例如查看ListenerList.DeliverEvent,这比通常的Invoke 调用要复杂一些。

基本上,普通事件系统假定强引用,因此为了使用WeakReference,您最终必须自己添加大量翻译逻辑,并决定如何处理由于使用WeakReference 而不会触发的事件。

【讨论】:

    猜你喜欢
    • 2013-12-24
    • 1970-01-01
    • 2013-08-15
    • 1970-01-01
    • 2015-01-17
    • 1970-01-01
    • 2010-12-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多