按照MEF的约定,任何一个类或者是接口的实现都可以通过[System.ComponentModel.Composition.ExportAttribute] 特性将其定义为组合部件(Composable Parts),在任何需要导入组合部件的地方都可以通过在特定的组合部件对象属性上使用[System.ComponentModel.Composition.ImportAttribute ]实现组合部件的导入,两者之间通过契约(Contracts)进行通信,实际上这一步可以简单的理解为“依赖注入”,本质上就是对象的实例初始化过程。

  我个人理解,凡是通过MEF的[ExportAttribute]标注的对象都可以理解为一个可进行组合的部件,包括对象和对象的属性、字段、方法、事件等;且该对象可以通过[ImportAttribute]进行导入。如下示例代码:

public class StringProvider
{
    /// <summary>
    /// 定义导出部件--契约为“Message”
    /// </summary>
    [Export("Message")]
    public string Output
    {
        get { return "Hello World"; }
    }
} public class Client
{
    /// <summary>
    /// 导入指定契约的部件
    /// </summary>
    [Import("Message")]
    public string Input { get; set; }     public void Print()
    {
        Console.WriteLine(Input);
    }
}

  所谓的契约也就是一种约定,或者叫做一种规则。如上代码中就使用到了契约,在对象StringProvider中就定义了一个导出部件属性(Output),并为其指定了通信契约为“Message”。这里的“Message”就是一种约定,既约定为在需要使用到这个属性的地方,都可以通过[ImportAttribute]使用契约(Message)进行部件的导入。

 

  接下来结合《Silverlight中使用CompositionInitializer宿主MEF》一文中使用的日志记录的应用实例为基础来看看关于契约(Contracts)在较为复杂的部件中的的具体使用方法。假设定义了如下的接口与部件实现代码:

public interface ILogger
{
    void WriteLog(string message);
}
    
[Export(typeof(ILogger))]
public class TXTLogger : ILogger
{
    public void WriteLog(string message)
    {
        MessageBox.Show("TXTLogger>>>>>" + message);
    }
} [Export(typeof(ILogger))]
public class DBLogger : ILogger
{
    public void WriteLog(string message)
    {
        MessageBox.Show("DBLogger>>>>>" + message);
    }
}

  对于熟悉面向对象设计方法的人一眼就能明白,上面代码演示了一个接口具有多个实现的场景,仔细观察会发现在每个实现类上面都添加了[ExportAttribute]将其标注为导出部件,并为其添加了通信契约,而且两个实现类的通信契约都是使用的接口(ILogger)的类型参数。

  这里需要注意的是在进行导入的时候如果辨别到底是使用的哪一个实现呢?在MEF中提供了一个专门用于导入多个实现的特性[System.ComponentModel.Composition.ImportManyAttribute],如上的日志实现示例就可以通过如下的方式实现多部件导入。

[ImportMany]
public IEnumerable<ILogger> Loggers { get; set; }

    

  ImportManyAttribute特性可以将实现接口的所有实现全部组合起来。下面为使用[ImportMany]的完整示例代码:

namespace MEFTraining.CPC
{
    public partial class MainPage : UserControl
    {
        [ImportMany]
        public IEnumerable<ILogger> Loggers { get; set; }         public MainPage()
        {
            InitializeComponent();             CompositionInitializer.SatisfyImports(this);
            if (Loggers == null)
            {
                foreach (var logger in Loggers)
                {
                    logger.WriteLog("Hello World");
                }
            }
        }
    }     public interface ILogger
    {
        void WriteLog(string message);
    }     [Export(typeof(ILogger))]
    public class TXTLogger : ILogger
    {
        public void WriteLog(string message)
        {
            MessageBox.Show("TXTLogger>>>>>" + message);
        }
    }     [Export(typeof(ILogger))]
    public class DBLogger : ILogger
    {
        public void WriteLog(string message)
        {
            MessageBox.Show("DBLogger>>>>>" + message);
        }
    }
}

  上面介绍了如何在相同的契约下获取所有导出部件的实例,在某种情况下或许我们就只直接指导需要使用那一种那个实现方式,那么是否可以通过直接指定一个“契约名”就可以从多个实现中获取到指定的组合部件呢?答案是肯定的,接下来先看看在MEF中中对ExportAttribute和ImportAttribute的定义,源代码如下:

public class ExportAttribute : Attribute
{
    public ExportAttribute() : this((string)null, (Type)null){}
    public ExportAttribute(Type contractType) : this((string)null, contractType){}
    public ExportAttribute(string contractName) : this(contractName, (Type)null) { }
    public ExportAttribute(string contractName, Type contractType)
    {
        this.ContractName = contractName;
        this.ContractType = contractType;
    }     public string ContractName { get; private set; }
    public Type ContractType { get; private set; }
}

  ImportAttribute同ExportAttribute一样提供了相同的重载构造函数,在将一个对象进行导出部件处理的时候可以直接通过ImportAttribute的属性给对象指定一个契约名,如本篇前面的日志组件的实现就可以修改为如下代码格式。

public interface ILogger
{
    void WriteLog(string message);
}
    
[Export("TXT", typeof(ILogger))]
public class TXTLogger : ILogger
{
    public void WriteLog(string message)
    {
        MessageBox.Show("TXTLogger>>>>>" + message);
    }
} [Export("DB", typeof(ILogger))]
public class DBLogger : ILogger
{
    public void WriteLog(string message)
    {
        MessageBox.Show("DBLogger>>>>>" + message);
    }
}

  通过为不同的导出部件指定了特定的契约名称,那么在装配部件的时候就可以通过契约名进行指定部件的装配并组合部件,为了方便调用可以提供一个服务类,将不同的实现通过不同的契约名装载组合起来以对系统提供一个统一的调用入口。以下为完整的示例代码:

public partial class MainPage : UserControl
{
    /// <summary>
    /// 导入日志服务对象
    /// </summary>
    [Import]
    public LogService Service { get; set; }     public MainPage()
    {
        InitializeComponent();         CompositionInitializer.SatisfyImports(this);         Service.DBLogger.WriteLog("Hello MEF");
        Service.TXTLogger.WriteLog("Hello MEF");
    }
} /// <summary>
/// 聚合不同的日志记录部件,通过MEF进行组合
/// </summary>
[Export]
public class LogService
{
    /// <summary>
    /// 根据契约名进行部件的装配
    /// </summary>
    [Import("TXT")]
    public ILogger TXTLogger { get; set; }     [Import("DB")]
    public ILogger DBLogger { get; set; }
} public interface ILogger
{
    void WriteLog(string message);
} [Export("TXT", typeof(ILogger))]
public class TXTLogger : ILogger
{
    public void WriteLog(string message)
    {
        MessageBox.Show("TXTLogger>>>>>" + message);
    }
} [Export("DB", typeof(ILogger))]
public class DBLogger : ILogger
{
    public void WriteLog(string message)
    {
        MessageBox.Show("DBLogger>>>>>" + message);
    }
}
 

【转】MEF程序设计指南三:MEF中组合部件(Composable Parts)与契约(Contracts)的基本应用的更多相关文章

  1. 《MEF程序设计指南》博文汇总

    <MEF程序设计指南>博文汇总 在MEF之前,人们已经提出了许多依赖注入框架来解决应用的扩展性问题,比如OSGI 实现以Spring 等等.在 Microsoft 的平台上,.NET Fr ...

  2. [zhuan]《MEF程序设计指南》博文汇总

    http://www.cnblogs.com/beniao/archive/2010/08/11/1797537.html 在MEF之前,人们已经提出了许多依赖注入框架来解决应用的扩展性问题,比如OS ...

  3. [转]《MEF程序设计指南》博文汇总

    在MEF之前,人们已经提出了许多依赖注入框架来解决应用的扩展性问题,比如OSGI 实现以Spring 等等.在 Microsoft 的平台上,.NET Framework 自身内部包含组件模型和 Sy ...

  4. [转]MEF程序设计指南

    <MEF程序设计指南>博文汇总 在MEF之前,人们已经提出了许多依赖注入框架来解决应用的扩展性问题,比如OSGI 实现以Spring 等等.在 Microsoft 的平台上,.NET Fr ...

  5. 【转】MEF程序设计指南四:使用MEF声明导出(Exports)与导入(Imports)

    在MEF中,使用[System.ComponentModel.Composition.ExportAttribute]支持多种级别的导出部件配置,包括类.字段.属性以及方法级别的导出部件,通过查看Ex ...

  6. MEF程序设计指南

    ############################################################################################## ##### ...

  7. 【转】MEF程序设计指南二:Silverlight中使用CompositionInitializer宿主MEF

    MEF可以在传统应用程序中使用(包括桌面的Winform.控制台程序和Web的ASP.NET),也可以在RIA的Silverlight中使用.在Silverlight中只是宿主的方式有所不同,实际上在 ...

  8. 【转】MEF程序设计指南一:在应用程序中宿主MEF

    在应用程序中宿主MEF其实非常简单,只需要创建一个组合容器对象(CompositionContainer)的实例,然后将需要组合的部件(Parts)和当前宿主程序添加到容器中即可.首先需要添加MEF框 ...

  9. MEF 编程指南(九):部件生命周期

    理解 MEF 容器部件生命周期和实现是非常重要的事情.考虑到 MEF 关注可扩展应用程序.这变得尤为重要.生命期可以解释为期望部件的共享性(transitively, its exports)   共 ...

随机推荐

  1. hbase建表时 ERROR: java.io.IOException: Table Namespace Manager not ready yet, try again later

    其实解决不难,是因为时钟不同步,把每个节点切换到root用户下同步时钟就好了,在重启hbase!

  2. APIView (DRF的视图)

    APIView和View的区别 -- APIView继承了View -- APIView 重写了as_view以及 dispatch方法 -- 在dispatch里重新封装了request  -- r ...

  3. 比较完整的URL验证

    转自:http://wuchaorang.2008.blog.163.com/blog/static/4889185220135279223253/ function IsURL(str_url){v ...

  4. 循环神经网络(RNN)

    1.    场景与应用 在循环神经网络可以用于文本生成.机器翻译还有看图描述等,在这些场景中很多都出现了RNN的身影. 2.    RNN的作用 传统的神经网络DNN或者CNN网络他们的输入和输出都是 ...

  5. springboot - SqlSessionFactoryBean falls in circular dependencies by Spring Boot's DataSourceInitializer

    springboot mybatis配置多数据源的时候,报错:There is a circular dependency between 7 beans in the application con ...

  6. eclipse web run on server 404

    eclipse真是个坑爹玩意儿,前期在idea开发的web,移到eclipse遇到各种问题 刚开始好好的,突然404,不明所以,搞了好几天 参考eclipse修改web项目部署路径 解决了问题 后来发 ...

  7. 使用MATPLOTLIB 制图(散点图,热力图)

    import numpy as np import pandas as pd import matplotlib.pyplot as plt data = pd.read_csv('D:\\myfil ...

  8. S+ hidden tray with window start

    官方论坛上有个帖子给出了答案: I forgot that this is supported in the code, but it requires a little editing of the ...

  9. Why We Worry and What to Do About It

    Note: My new book Atomic Habits is available to preorder now. Click here to learn more. The Evolutio ...

  10. 表单如何与Servlet控制器关联

    <form action="loing.do" method="post">                <div>          ...