我在MEF的零件寿命中遇到了一些问题,这会导致我的Prism应用程序内存泄漏。
我的应用程序导出视图和视图模型,其中PartCreationPolicy
设置为CreationPolicy.NonShared
。视图和视图模型分别继承了实现IDisposable
的ViewBase
和ViewModelBase
。
现在,由于我的部分实现了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
。当然,这可能不适合所有人。