MEF保留对非共享IDisposable部分的引用,不允许GC收集它们



我在MEF的零件寿命中遇到了一些问题,这会导致我的Prism应用程序内存泄漏。

我的应用程序导出视图和视图模型,其中PartCreationPolicy设置为CreationPolicy.NonShared。视图和视图模型分别继承了实现IDisposableViewBaseViewModelBase

现在,由于我的部分实现了IDisposable,容器保留了对它们的引用,这导致垃圾收集器不会释放它们。根据MEF关于零件寿命的文件,这是经过设计的:

除非以下情况之一为真,否则容器将不会保存对其创建的零件的引用:

  • 零件标记为Shared
  • 该部分实现IDisposable
  • 一个或多个导入配置为允许重新组合

那么我怎么能让MEF不保留对这些部分的引用呢?有没有一个属性可以让MEF知道,即使它实现了IDisposable,我也不希望它保留对我的零件的引用?

上面文章中讨论的两种策略对我来说似乎都不是很好的解决方案:

  • ReleaseExport需要一个Export对象作为参数,我不知道如何提供。我有我的视图的实例,但我无法知道用于创建视图的合同是什么。如果ReleaseExport有一个重载,它可以接收容器创建的任何对象,那就太好了
  • 使用子容器似乎也不是一个自然的选择

任何帮助都将不胜感激。

除非Prism支持视图对象的某种生存期,否则除了从视图公开的接口列表中删除IDisposable之外,这里没有任何解决方案。

有三种MEF方法来处理这个问题,其他响应者都提到了:

  • ExportFactory<T>
  • 儿童容器
  • ReleaseExport()

所有这些都需要对请求原始导出的代码部分进行一些工作——在本例中是Prism中的代码。这是有道理的,因为使用对象的代码必须知道它是如何以及何时创建的,这是不可取的。

MEF中没有ReleaseExportedObject(),因为多个(例如属性)导出可以返回相同的值;在逻辑上可能提供,但是增加的复杂性使得它在可预见的未来不太可能由MEF解决。

希望这能有所帮助;我把这个问题重新标记为"棱镜",因为我相信棱镜社区的其他人也会遇到这个问题,并能够提供建议。

当您实现IDisposable时,您有点说应该以确定性的方式清理类型(通过调用IDisposable.Dispose,而不是在垃圾收集器决定时间到时随机调用)。

在你的情况下,视图模型只会在你处理容器时被处理,这可能不是你想做的。为了解决这个问题,我看到了两种可能的解决方案:

  • 不要在视图模型上实现IDisposable。显然,你根本不在乎它们什么时候被清理干净,为什么要把它们做成IDisposable呢?

  • 您可以使用共享视图模型工厂类,而不是让容器创建每个非共享的视图模型。然后,您可以将该类注入视图模型的所有者中,以允许所有者显式地创建视图模型。大概这些所有者也会知道何时处置视图模型。

基本上,如果某个东西是一次性的,那么这也应该是代码中需要处理一次性东西的一个合理点。

您应该通过导入的ExportFactory<T>。然后,您将拥有通过ExportLifetimeContext<T>.Dispose()处理它们的必要控制权。

但是,这仅在下一个.NET版本(4.5)或codeplex上的最新MEF预览版中提供。(在旧版本的MEF中,相同的功能作为示例实现,并被称为PartCreator,如本文所述。)

所有其他答案都提供了规避此问题的好方法,但我最终使用了自己的自定义接口ICleanup,而不是IDisposable。当然,这可能不适合所有人。

最新更新