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

.net MEF保留非共享IDisposable部分的引用,不允许GC收集它们,.net,memory-leaks,prism,mef,object-lifetime,.net,Memory Leaks,Prism,Mef,Object Lifetime,我在MEF的部件生命周期中遇到了一些问题,导致我的Prism应用程序内存泄漏 我的应用程序导出视图和视图模型时,PartCreationPolicy设置为CreationPolicy.NonShared。视图和视图模型分别继承自ViewBase和ViewModelBase,后者实现了IDisposable 现在,由于我的部件实现了IDisposable,容器保留了对它们的引用,这导致垃圾收集器不会释放它们。根据设计,这是: 除非满足以下条件之一,否则容器将不包含对其创建的零件的引用: 零件标记

我在MEF的部件生命周期中遇到了一些问题,导致我的Prism应用程序内存泄漏

我的应用程序导出视图和视图模型时,
PartCreationPolicy
设置为
CreationPolicy.NonShared
。视图和视图模型分别继承自
ViewBase
ViewModelBase
,后者实现了
IDisposable

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

除非满足以下条件之一,否则容器将不包含对其创建的零件的引用:

  • 零件标记为
    共享
  • 该部件实现了
    IDisposable
  • 一个或多个导入配置为允许重新编译
那么,我如何才能使MEF不保留对这些部分的引用?是否有一个属性可以让MEF知道我不希望它保留对我的零件的引用,即使它实现了
IDisposable

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

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

任何帮助都将不胜感激。

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

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

  • 不要在您的视图模型上实现
    IDisposable
    。显然,您并不关心它们何时被清理,那么为什么要将它们设置为
    IDisposable

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


基本上,如果某个实例是一次性的,那么在您的代码中也应该是一个合理的点,在这里您需要处理一次性的内容。

您应该通过导入的来创建这些实例。然后,您将拥有必要的控制权,可以通过
ExportLifetimeContext.dispose()来处理它们。


但是,这仅在下一个.NET版本(4.5)或codeplex上的最新MEF预览版本中提供。(在旧版本的MEF中,相同的功能作为示例实现,称为PartCreator,如本节所述。)除非Prism支持视图对象的某种生存期,否则这里没有解决方案,除非从视图公开的接口列表中删除
IDisposable

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

  • ExportFactory
  • 子容器
  • ReleaseExport()
所有这些都需要对请求原始导出的代码部分进行一些工作—在本例中是Prism中的代码。这有一定的意义,因为使用对象的代码不需要知道对象是如何创建的以及何时创建的

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


希望这能有所帮助;我重新标记了这个问题“prism”,因为我相信prism社区中的其他人也会遇到这个问题,并且能够提供建议。

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

我相信你会同意,我实现
IDisposable
是有点愚蠢,因为我的虚拟机上实现了MEF容器,所以我早一点而不是晚一点处理一个对象,并实现了完全相反的效果。不实现
IDisposable
,这不是一个错误eal选项,因为我需要像从复合命令中注销一样进行清理-如果我不这样做,虚拟机将不会被GC’d。我的视图负责处理它们的虚拟机,但无论如何调用
Dispose()
不会从容器中删除对对象的引用,因此无论如何也不会有帮助。我认为,使用我的第二个建议,即在每个视图中注入视图模型工厂,而不是MEF创建的视图模型,应该可以解决您的问题。显然,为每个视图模型创建工厂会变得相当乏味,但这就是问题所在我发现自己在使用当前版本的MEF进行依赖项注入时做了很多工作。我在问题中没有提到它,但我使用Prism创建视图,因此我无法访问代码的这一部分来检索
ExportFactory
对象。即使可以,也存在从中的位置访问factory对象的问题在要处理视图的位置编码,并对每个
非共享的文件使用
ExportFactory