• C#可扩展编程之MEF学习笔记(五):MEF高级进阶


    好久没有写博客了,今天抽空继续写MEF系列的文章。有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后。

    前面四篇讲了MEF的基础知识,学完了前四篇,MEF中比较常用的基本已经讲完了,相信大家已经能看出MEF所带来的便利了。今天就介绍一些MEF中一些较为不常用的东西,也就是大家口中的所谓的比较高级的用法。

    前面讲的导出都是在每个类上面添加Export注解,实现导出的,那么有没有一种比较简便的方法呢?答案是有的,就是在接口上面写注解,这样只要实现了这个接口的类都会导出,而不需要在每个类上面都写注解。下面仅贴出接口和一个实现类的源码,其余的模仿即可:

    接口代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel.Composition;
    
    namespace BankInterface
    {
       [InheritedExport]
       public interface ICard
       {
          //账户金额
          double Money { get; set; }
          //获取账户信息
          string GetCountInfo();
          //存钱
          void SaveMoney(double money);
          //取钱
          void CheckOutMoney(double money);
       }
    
    }

    接口上面添加了[InheritedExport]标记,没错,这个就是用在接口上面的注解。

    下面给出一个实现类的代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using BankInterface;
    using System.ComponentModel.Composition;
    
    namespace BankOfChina
    {
       //[Export(typeof(ICard))]
       public class ZHCard : ICard
       {
          public string GetCountInfo()
          {
             return "Bank Of China";
          }
    
          public void SaveMoney(double money)
          {
             this.Money += money;
          }
    
          public void CheckOutMoney(double money)
          {
             this.Money -= money;
          }
    
          public double Money { get; set; }
       }
    }

    可以看到,我注释掉了导出的注解,运行后,依然可以看到,此类还是被导出了,运行结果相信看过上一篇的都已经知道了。

    注意:这种方法虽然比较简单,但是只适用于比较简单的应用,看完下面后,相信大家会意识到他的不足。

    下面进入今天的重点:

     MEF中如何访问某个具体的对象

      前面我们讲过在导出的时候,可以在[Export()]注解中加入名称标识,从而识别某个具体的对象,然而这种方法只是用于页面初始化的时候就行过滤,页面打开后没有导入的就再也导入不了了,就是说我们不能在导入的集合中分辨各自的不同,所有导入的类都是没有标识的。

      为了给每一个类添加标识,我们要继承ExportAttribute类,为他添加标识属性MetaData,首先来写继承自ExportAttribute的类,代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel.Composition;
    
    namespace BankInterface
    {
       /// <summary>
       /// AllowMultiple = false,代表一个类不允许多次使用此属性
       /// </summary>
       [MetadataAttribute]
       [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
       public class ExportCardAttribute : ExportAttribute
       {
          public ExportCardAttribute()
             :base(typeof(ICard))
          {
          }
    
          public string CardType { get; set; }
       }
    }

    代码很简单,调用的父类的构造方法,声明了一个属性CatdType,下面来添加一个接口,直接修改ICard接口文件,代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel.Composition;
    
    namespace BankInterface
    {
       public interface ICard
       {
          //账户金额
          double Money { get; set; }
          //获取账户信息
          string GetCountInfo();
          //存钱
          void SaveMoney(double money);
          //取钱
          void CheckOutMoney(double money);
       }
    
       public interface IMetaData
       {
          string CardType { get;}
       }
    }

    又添加了接口IMetaData,只有一个属性,注意这个属性要和刚写的ExportCardAttribute类中的属性名称要一致,这样才能实现导出。

    下面利用我们的ExportCardAttribute属性来标记我们要导出的类:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using BankInterface;
    using System.ComponentModel.Composition;
    
    namespace BankOfChina
    {
       [ExportCardAttribute(CardType="BankOfChina")]
       public class ZHCard : ICard
       {
          public string GetCountInfo()
          {
             return "Bank Of China";
          }
    
          public void SaveMoney(double money)
          {
             this.Money += money;
          }
    
          public void CheckOutMoney(double money)
          {
             this.Money -= money;
          }
    
          public double Money { get; set; }
       }
    }

    在这里,我们可以设置CardType的属性,可以根据具体情况使用不同的数据类型。

    现在,我们修改主程序的代码为:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection;
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    using BankInterface;
    
    namespace MEFDemo
    {
       class Program
       {
          //其中AllowRecomposition=true参数就表示运行在有新的部件被装配成功后进行部件集的重组.
          [ImportMany(AllowRecomposition = true)]
          public IEnumerable<Lazy<ICard,IMetaData>> cards { get; set; }
    
          static void Main(string[] args)
          {
             Program pro = new Program();
             pro.Compose();
             foreach (var c in pro.cards)
             {
                if (c.Metadata.CardType == "BankOfChina")
                {
                   Console.WriteLine("Here is a card of Bank Of China ");
                   Console.WriteLine(c.Value.GetCountInfo());
                }
                if (c.Metadata.CardType == "NongHang")
                {
                   Console.WriteLine("Here is a card of Nong Ye Yin Hang ");
                   Console.WriteLine(c.Value.GetCountInfo());
                }
             }
             Console.Read();
          }
    
          private void Compose()
          {
             var catalog = new DirectoryCatalog("Cards");
             var container = new CompositionContainer(catalog);
             container.ComposeParts(this);
          }
       }
    }

    这里我用到了Lazy延迟加载机制(具体参见Lazy延迟加载),可以看到我们可以根据MetaData的属性访问到CardType属性,从而判断出Card的类型,从而区分导入的类型。

    点击这里,下载源码

    MEF系列文章:

     C#可扩展编程之MEF学习笔记(一):MEF简介及简单的Demo

    C#可扩展编程之MEF学习笔记(二):MEF的导出(Export)和导入(Import)

    C#可扩展编程之MEF学习笔记(三):导出类的方法和属性

    C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻

    C#可扩展编程之MEF学习笔记(五):MEF高级进阶

  • 相关阅读:
    算法与数据结构基础
    算法与数据结构基础
    算法与数据结构基础
    算法与数据结构基础
    算法与数据结构基础
    算法与数据结构基础
    最佳实践 根据状态操作,这样能避免吃掉异常
    最佳实践 状态设计
    Android HTTPS如何10分钟实现自签名SSL证书
    马桶选购
  • 原文地址:https://www.cnblogs.com/yunfeifei/p/3991330.html
Copyright © 2020-2023  润新知