MEF如何获取非共享实例引用?

3

最近,在我的WPF应用程序中使用MEF时遇到了一个问题。我创建了几个类,如下所示。Part类型被设置为CreationPolicy.NonShared,这样将有2个不同的对象导入到ClassA和ClassB中。

[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class Part
{
    public int Id { get; set; }
}

[Export]
public class ClassA
{
    [Import]
    public Part PartA { get; set; }
}

[Export]
public class ClassB
{
    [Import]
    public Part PartB { get; set; }
}

我写了一小段代码来描述我的问题,如下所示。

[Export]
class Program
{
    [Import]
    public ClassA A { get; set; }

    [Import]
    public ClassB B { get; set; }

    [ImportMany(AllowRecomposition = true)]
    public IEnumerable<Part> AllParts { get; set; }

    static void Main(string[] args)
    {
        var catalog = new AssemblyCatalog(typeof(Program).Assembly);
        var container = new CompositionContainer(catalog);

        var prog = container.GetExportedValue<Program>();

        foreach (var part in prog.AllParts)
        {
            // Do something for Part instances.
            // I want to get all Part instances created by MEF which have imported to ClassA and ClassB.
            // However, it comes a list with a brand new Part instance.
        }
    }
}

所以会有ClassA,ClassB和一系列Part实例导入到Program对象中。实际上,我想得到的是MEF容器创建的所有Part实例。然而,它返回了一个全新的Part实例列表。
我知道这可能是因为我在Part类中指定了CreationPolicy.NonShared。但是,即使我尝试在container.Catalog.Parts中查找它们,我也只找到了一个Part实例。这让我感到困惑。根据我的理解,容器应该持有它创建的所有对象的引用,因为我已经指定了AllowRecomposition = true。我找到了一篇文章来证明这一点。它说:

容器和部件引用

我们认为 .Net 垃圾回收器是可靠的清理工具,但我们也需要提供一个具有确定性行为的容器。因此,容器不会保留对其创建的部件的引用,除非以下情况之一成立:

  • 该组件标记为共享
  • 该组件实现IDisposable接口
  • 一个或多个导入被配置为允许重新组合

对于这些情况,将保留一个部件引用。结合您可以拥有非共享的部件并继续请求这些部件的事实,内存需求可能很快成为一个问题。为了缓解这个问题,您应该依赖下面两个主题中讨论的以下策略之一。

所以我有2个问题:

  1. 为什么在容器中找不到超过一个 Part 类型的实例?

  2. 如何在我的演示中获取所有导出的 Part 实例?

1个回答

4
< p > ImportMany 并不是你想象的那样。

请考虑以下内容:

public interface IMyInterface
{
    int Id { get; }
}

[Export(typeof(IMyInterface))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class Part1 : IMyInterface
{
    public int Id { get; private set; }
}

[Export(typeof(IMyInterface))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class Part2 : IMyInterface
{
    public int Id { get; private set; }
}

[Export]
public class ClassA
{
    [ImportMany]
    public IEnumerable<IMyInterface> Parts { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var catalog = new AssemblyCatalog(typeof(Program).Assembly);
        var container = new CompositionContainer(catalog);

        var a = container.GetExportedValue<ClassA>();
        // ...
    }
}

此时,a.Parts 属性将包含两个实例:一个 Part1 和一个 Part2
因此,ImportMany 的目的不是为了获取所有先前导出的实例,而是为了获取每个导出您的接口的新实例。
至于您如何获得所有导出的非共享实例的问题,我认为这是不可能的,因为这正是其为非共享的整个意义所在。

感谢您的回复。从您所说的,您是不是意味着MEF目前只是一个工厂?我认为作为容器的MEF应该以某种方式持有引用。请注意我的问题中的引号。它还说即使在某些情况下未共享导出,容器也会保留引用。 - Lucax
你的代码对于对象创建很好。然而,我实际想要做的是获取所有非共享导出项以便在一个地方执行一些常见操作。 - Lucax
2
关于问题中的引用,内部持有引用并不一定意味着你可以以列表的方式获取它们。也许它们只能用于重组。 - wkl
@Lucax 我理解了你的问题,正如我在我的答案中所说,我不认为这是可能的。(即使是可能的,也绝对不会使用 ImportMany 来完成。)也许如果你能给我们更多关于你想要实现什么(例如,为什么你需要那些实例),我们可以给你更好的建议。 - Venemo
谢谢,Venemo。在您的帮助下,我发现对于非共享实例来说这是不可能做到的。最终,我改变了我的方案,将它们设置为共享实例。 - Lucax

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接