通过静态属性组合具有不同导出名称的共享部件

时间:2013-02-25 09:31:16

标签: c# static mef .net-4.5 composition

需要 -

  1. 声明同一接口的共享导出。导出由唯一的导出名称标记,因此消费者可以导入导出的特定风格。
  2. 将类的公共实例注入一组对象,但不跨对象组共享一个公共实例[这使我使用不同的键使用共享导出 - 一组对象可以使用单个键来满足他们的共享导入需要]
  3. 这是导出类

    public interface IMyExport
    {
        void Display();
    }
    
    public class MyExport : IMyExport
    {
        private Guid _id = Guid.NewGuid();
    
        public void Display()
        {
            Console.WriteLine("Instance ID = "+_id);
        }
    }
    

    以下是我导出类

    的实例的方法
    public static class ExportInitialization
    {
        [Export("Type A", typeof(IMyExport)),
        Export("Type B", typeof(IMyExport))]
        public static IMyExport IceCreamExport
        {
            get
            {
                return new MyExport();
            }
        }
    }
    

    消费者可以通过以下方式导入特定实例

    [Export]
    public class ImporterA
    {
        private readonly IMyExport _myExport;
    
        [ImportingConstructor]
        public ImporterA([Import("Type A")]IMyExport myExport)
        {
            _myExport = myExport;
        }
    
        public void Display()
        {
            _myExport.Display();
        }
    }
    
    [Export]
    public class ImporterB
    {
        private readonly IMyExport _myExport;
    
        [ImportingConstructor]
        public ImporterB([Import("Type B")]IMyExport myExport)
        {
            _myExport = myExport;
        }
    
        public void Display()
        {
            _myExport.Display();
        }
    }
    
    class Program
    {
        [Import]
        public ImporterA ImporterA { get; set; }
    
        [Import]
        public ImporterB ImporterB { get; set; }
    
        static void Main(string[] args)
        {
            new Program().Run();
        }
    
        public void Run()
        {
            var container = new CompositionContainer(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
            container.ComposeParts(this);
    
            ImporterA.Display();
            ImporterB.Display();
    
            Console.ReadKey();
        }
    }
    

    这曾经与.Net 4.0一起使用,但是当安装.Net 4.5时 - 我得到以下ouptut

    Instance ID = 78bba41a-0c48-44fc-ae69-f0ead96371f9
    Instance ID = 78bba41a-0c48-44fc-ae69-f0ead96371f9
    

    请注意,两个导入都返回相同的对象实例。我是否打破了一些关于通过静态属性导出的无证规则?

2 个答案:

答案 0 :(得分:1)

我发现从两个不同的静态属性导出特定实例可确保返回2个不同的实例。

    [Export("Type A", typeof(IMyExport))]
    public static IMyExport ExportA
    {
        get
        {
            return new MyExport();
        }
    }

    [Export("Type B", typeof(IMyExport))]
    public static IMyExport ExportB
    {
        get
        {
            return new MyExport();
        }
    }

这很令人费解,因为在未经修改的版本中,静态getter正在每次获取时创建一个新实例。不确定这是否是4.5引入的某些C#/。Net优化的结果,或者这是否是MEF问题

答案 1 :(得分:0)

这与MEF parts lifetime

有关

MEF属性的默认值是组件不会说他们是否每次都要关注新实例。

意思是:

  • 您的ExportAttribute未指定是否可以或应该共享导出的实例;
  • 两个ImportAttribute都未指定是否应共享其导入;

MEF的默认行为是,如果不禁止共享实例,它将会。这意味着,根据文档,.NET 4.5的行为是正确的:MyExport的实例是共享的,因为任何一方都没有明确禁止共享。

我认为.NET 4.0有一个错误/差异,每次调用静态属性,导致你观察到的,即非共享实例。你依赖那个bug。我认为这个bug的起源是对于属性的一个基本的,框架范围的期望 - 让静态属性为每个属性调用创建一个新的,语义上不同的实例是非常罕见的。

我相信你应该:

  1. 使用静态方法导出替换静态属性导出;
  2. 在导出端或导入端指定创建策略为非共享;