MEF编程模型

  1. Contract由Contract name和Contract type组成,Contract两个参数可以省略可以implicit也可以explicit,implicit时name和type会自动推断
  2. 某些情况必须要指定contract name,最常见的情况是一个类export多个相同类型的成员,即使显示指定了contract name,Import和Export的contract type也要匹配 
    public class MyClass {
    [Import(
    "MajorRevision"
    )]
    public int MajorRevision { get; set
    ; }
    }
    public class
    MyExportClass
    {
    [Export(
    "MajorRevision")] //This one will match.
    public int MajorRevision = 4
    ;
    [Export(
    "MinorRevision"
    )]
    public int MinorRevision = 16
    ;
    }
  3. 通常应该对公共类或者成员声明导出和导入,其他声明也受支持,但如果导出或导入私有成员,受保护成员或者内部成员,将会损坏部件的隔离模型,因此建议不要这样做.
  4. 导入的类型: 导入多个对象:如果要导入与多个contract匹配的导出,可以使用ImportMany attribute,用此attribute标记的导入始终是可选导入
    • 动态导入:导入类可能需要与任何具有相同contract name的导出类匹配,此时可以使用dynamic关键字声明动态导入,contract会自动推导,如下示例代码,两个导出均与前面的导入匹配,但要注意如果这两个部件都加到了Catalog中,在执行组合时会抛异常:More than one export was found that matches the constraint:
      public class MyClass {
      [Import(
      "TheString"
      )]
      public dynamic MyAddin { get; set
      ; }
      }

      [Export(
      "TheString", typeof
      (IMyAddin))]
      public class
      MyLogger : IMyAddin { }

      [Export(
      "TheString"
      )]
      public class MyToolbar { }
    • 延迟导入:导入类需要间接引用导入的对象,不会立即实例化该对象,此时可以使用Lazy<T>来声明延迟导入,Lazy关键字会使对象的创建延迟到使用时创建,如下示例代码,Lazy<IMyAddin>与IMyAddin被视为相同的contract type
      public class MyClass {
      [Import]
      public Lazy<IMyAddin> MyAddin { get; set
      ; }
      }

      [Export(
      typeof
      (IMyAddin))]
      public class MyLogger : IMyAddin { }
    • 必备导入:MEF容器创建部件时默认使用部件的无参构造函数,如果要使组合引擎使用其他构造函数,必须用ImportingConstructor标记此构造函数,每个部件只有一个构造函数供组合引擎使用,如果不指定默认构造函数和ImportingConstructor attribute或者提供了多个ImportingConstructor,都会产生错误.如果C#类没有任何构造函数,则会自动生成一个默认构造函数(无参),如果有其他构造函数就不会产生默认构造函数。
      public class MyClass
      {
      private
      IMyAddin _theAddin;
      public
      MyClass() { }

      //
      This constructor will be used.
      //
      An import with contract type IMyAddin is
      //declared automatically.
      [ImportingConstructor]
      public
      MyClass(IMyAddin MyAddin)
      {
      _theAddin
      =
      MyAddin;
      }
      }
      • 使用ImportingConstructor标记的构造函数的参数都会自动声明为必备导入.默认情况下会为所有参数自动推导contract type和contract name,但可以使用Import显示指定type和name:
        [ImportingConstructor]
        public MyClass([Import(typeof
        (IMySubAddin))]IMyAddin MyAddin)
        {
        _theAddin
        =
        MyAddin;
        }
      • 如果使用ImportingConstructor标记的构造函数的参数是集合类型,则导出将与集合中对象的类型匹配,此时必须使用ImportMany修饰参数
    • 可选导入:默认情况下如果没有与导入匹配的导出,组合会失败,此时可以使用AllowDefault property指定导入为可选,这样组合也将成功,导入属性将被设为其属性类型的默认值
      public class MyClass
      {
      [Import(AllowDefault
      = true
      )]
      public Plugin thePlugin { get; set
      ; }
      //
      If no matching export is avaliable,
      //thePlugin will be set to null.
      }
  5. public class MyClass
    {
    [ImportMany]
    public IEnumerable<IMyAddin> MyAddin { get; set
    ;}
    }
  6. 避免发现:为了使部件不作为目录的一部分不被发现,可以使用两种方式: 元数据:导出提供的关于其自身的额外信息,元数据可以将导出对象的属性传递到导入部件,导入部件可以使用这些数据来决定使用哪些导出,或者只收集导出的信息而不构造导出。导入必须为延迟导入才能使用元数据
    元数据视图:为了使用元数据通常会声明一个称为元数据视图的接口,该接口声明声明元数据将可用,元数据视图接口必须只有属性,并且这些属性有get访问器.通常在元数据视图中命名的所有属性都是必须的,并且不会将未提供这些属性的任何导出视为匹配。DefaultValue attribute指定属性是可选的.如果未包括属性,则将为其分配使用DefaultValue 指定的默认值
    • 将部件类声明为abstract,抽象类不提供导出,即使使用Export标记了此类
    • 使用PartNotDiscoverable attribute标记类,这样此部件不会被包括在任何目录中
      [Export]
      public class
      DataOne
      {
      //
      This part will be discovered
      //as normal by the catalog.
      }
      [Export]
      public abstract class
      DataTwo
      {
      //
      This part will not be discovered
      //by the catalog.
      }
      [PartNotDiscoverable]
      [Export]
      public class
      DataThree
      {
      //
      This part will also not be discovered
      //by the catalog.
      }
  7. //元数据视图
    public interface
    IPluginMetadata
    {
    //元数据
    string Name { get
    ; }
    [DefaultValue(
    1
    )]
    int Version { get
    ; }
    }

    [Export(
    typeof
    (IPlugin)), ExportMetadata("Name", "Logger" ), ExportMetadata("Version", 4 )]
    public class
    Logger : IPlugin { }

    [Export(
    typeof
    (IPlugin)), ExportMetadata("Name", "Disk Writer" )]
    //Version is not required because of the DefaultValue
    public class
    DWriter : IPlugin { }

    public class
    Addin
    {
    [Import]
    public Lazy<IPlugin, IPluginMetadata>
    plugin;
    }
  8. 多情况下元数据与ImportMany attribute结合使用,以便分析各个可用的导入并选择实例化一个导入
    public class User
    {
    [ImportMany]
    public IEnumerable<Lazy<IPlugin, IPluginMetadata>>
    plugins;
    public
    IPlugin InstantiateLogger ()
    {
    IPlugin logger
    = null
    ;
    foreach (Lazy<IPlugin, IPluginMetadata> plugin in
    plugins)
    {
    if (plugin.Metadata.Name = "Logger")
         logger =
    plugin.Value;
    }
    return
    logger;
    }
    }
  9. 导入导出继承:导入始终由子类继承,而使用Export声明的导出则不会由子类继承,部件只有使用InheritedExport attribute标记自身,子类才能继承导出。InheritedExport只能作用于类级别,因此成员级别导出永远不会被继承.     
    如果存在于InheritedExport关联的元数据,该元数据也将被继承,子类无法修改继承的元数据,但通过使用相同contract重新声明InheritedExport,可以替换继承的元数据。注意contract必须相同否则相当于创建了一个新的导出
    [InheritedExport(typeof(IPlugin)), ExportMetadata("Name", "Logger"), ExportMetadata("Version", 4)]
    public class
    Logger : IPlugin
    {
    //
    Exports with contract type IPlugin and
    //metadata "Name" and "Version".
    }
    public class
    SuperLogger : Logger
    {
    //
    Exports with contract type IPlugin and
    //
    metadata "Name" and "Version", exactly the same
    //as the Logger class.
    }
    [InheritedExport(
    typeof
    (IPlugin)), ExportMetadata("Status", "Green")]
    public class
    MegaLogger : Logger
    {
    //
    Exports with contract type IPlugin and
    //
    metadata "Status" only. Re-declaring
    //the attribute replaces all metadata.
    }
  10. 自定义导出特性:通过继承Export或InheritedExport可以扩展export attribute,以包含元数据作为特性属性,自定义attribute可以指定contract type,contract name,或任何其他元数据。自定义的attribute类必须使用MetadataAttribute attribute标记,使用MetadataAttribute标记的类中的所有属性都被视为自定义attribute中定义的元数据,如果要创建可选元数据可以使用DefaultValue attribute.
    [MetadataAttribute]
    [AttributeUsage(AttributeTargets.Class, AllowMultiple
    =false
    )]
    public class
    MyAttribute : ExportAttribute
    {
    public MyAttribute(string
    myMetadata) : base(typeof(IMyAddin))
    {
    MyMetadata
    =
    myMetadata;
    }
    public string MyMetadata { get; private set
    ; }
    }

    //隐式指定contract type和元数据
    [MyAttribute("theData"
    )]
    public MyAddin myAddin { get; set; }
     
  11. 创建策略:分两种shared和non-shared,表示策略的枚举类型CreationPolicy有三个值Shared,NonShared,Any,Any会根据上下文选用适当的CreateionPolicy,默认为Shared. 创建策略为Shared的部件将在每个导入之间共享
    • 导入和导出都可以使用CreatePolicy指定部件的创建策略
    • 指定Shared或NonShared的导出将仅与指定相同值或指定Any的导入匹配
    • 指定Shared或NonShared的导入将仅与指定相同值或指定Any的导出匹配
    • 未指定CreationPolicy默认为Any,Any的策略默认为Shar
      [Export]
      public class PartOne
      {
          //The default creation policy for an export is Any.
      }
      
      public class PartTwo
      {
          [Import]
          public PartOne partOne { get; set; }
      
          //The default creation policy for an import is Any.
          //If both policies are Any, the part will be shared.
      }
      
      public class PartThree
      {
          [Import(RequiredCreationPolicy = CreationPolicy.Shared)]
          public PartOne partOne { get; set; }
      
          //The Shared creation policy is explicitly specified.
          //PartTwo and PartThree will receive references to the
          //SAME copy of PartOne.
      }
      
      [Export]
      [PartCreationPolicy(CreationPolicy.NonShared)]
      public class PartFour
      {
          //The NonShared creation policy is explicitly specified.
      }
      
      public class PartFive
      {
          [Import]
          public PartFour partFour { get; set; }
      
          //The default creation policy for an import is Any.
          //Since the export's creation policy was explictly
          //defined, the creation policy for this property will
          //be non-shared.
      }
      
      public class PartSix
      {
          [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
          public PartFour partFour { get; set; }
      
          //Both import and export specify matching creation 
          //policies.  PartFive and PartSix will each receive 
          //SEPARATE copies of PartFour, each with its own
          //internal state.
      }
      
      public class PartSeven
      {
          [Import(RequiredCreationPolicy = CreationPolicy.Shared)]
          public PartFour partFour { get; set; }
      
          //A creation policy mismatch.  Because there is no 
          //exported PartFour with a creation policy of Shared,
          //this import does not match anything and will not be
          //filled.
      }
  12. 生命周期和释放:部件应该根据自身需要实现IDisposable接口,由于容器创建并维护部件的引用,所以只有容器才能调用部件的Dispose方法,容器本身也实现了IDisposable接口。容器也提供了ReleaseExport用于释放nonshared部件.IPartImportsSatisfiedNotification包含一个名为OnImportsSatisfied的方法。如果部件实现了此接口,当部件成功导入时,就会调用此方法,某些情况可以替代构造函数执行初始化操作
posted on 2014-09-12 14:24 哨兵 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/phenixyu/p/3968332.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值