zoukankan      html  css  js  c++  java
  • C#进阶のMEF注入

    1、什么是MEF

    先来看msdn上面的解释:MEF(Managed Extensibility Framework)是一个用于创建可扩展的轻型应用程序的库。 应用程序开发人员可利用该库发现并使用扩展,而无需进行配置。 扩展开发人员还可以利用该库轻松地封装代码,避免生成脆弱的硬依赖项。 通过 MEF,不仅可以在应用程序内重用扩展,还可以在应用程序之间重用扩展。

    也有人把MEF解释为“依赖注入”的一种方式,那么什么是“依赖注入”?如果这样解释,感觉越陷越深......根据博主的理解,了解MEF只需要抓住以下几个关键点:

    (1)可扩展的framework,或者叫可扩展的库。也就是说,使用MEF是为了提高程序的可扩展性。MEF会根据指定的导入导出自动去发现匹配的扩展,不需要进行复杂的程序配置。

    (2)在设计层面上来说,为什么要使用MEF?为了“松耦合”!我们知道,程序设计有几个原则,“高内聚,低耦合”就是其中一个。使用MEF可以帮助我们减少内库之间的耦合。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection;
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    namespace MEFDemo
    {
      class Program
      {
        [Import("MusicBook")]
        public IBookService Service { get; set; }
        static void Main(string[] args)
        {
          Program pro = new Program();
          pro.Compose();
          if (pro.Service != null)
          {
            Console.WriteLine(pro.Service.GetBookName());
          }
          Console.Read();
        }
        private void Compose()
        {
          var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
          CompositionContainer container = new CompositionContainer(catalog);
          container.ComposeParts(this);
        }
      }
    }
    修改MusicBook的代码如下:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel.Composition;
    namespace MEFDemo
    {
      [Export("MusicBook",typeof(IBookService))]
      public class MusicBook : IBookService
      {
        public string BookName { get; set; }
        public string GetBookName()
        {
          return "MusicBook";
        }
      }
    }
    注意,标红的是改动过的地方,其他地方的代码没有变,上一次我们使用的是Export的方法是 [Export(typeof(IBookService))] ,这次前面多了一个参数,没错,这个就是一个契约名,名字可以随便起,而且可以重复,但是如果名字乱起,和其他DLL中的重复,到时候会导致程序出现很多Bug,最好按照一定的规范去起名字。
    这里有了契约名以后,导入(Import)时就要指定的契约名,否则将无法找到MusicBook,Export还有一个方法是 [Export("Name")], 这个方法只指定了契约名,没有指定导出类型,那么默认的导出类型是object类型,在导入时导出到的对象就要为object类型,否则将匹配不到那个组件。
    到现在,我们只写了一个接口和一个实现类,导出的也是一个类,下面我们多添加几个类来看看会怎么样,为了方便大家测试,我把实现接口的类写在一个文件里面,新加几个类后,的MusicBook类文件代码如下:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel.Composition;
    namespace MEFDemo
    {
      [Export("MusicBook",typeof(IBookService))]
      public class MusicBook : IBookService
      {
        public string BookName { get; set; }
        public string GetBookName()
        {
          return "MusicBook";
        }
      }
      [Export("MusicBook", typeof(IBookService))]
      public class MathBook : IBookService
      {
        public string BookName { get; set; }
        public string GetBookName()
        {
          return "MathBook";
        }
      }
      [Export("MusicBook", typeof(IBookService))]
      public class HistoryBook : IBookService
      {
        public string BookName { get; set; }
        public string GetBookName()
        {
          return "HistoryBook";
        }
      }
    }
    这里添加两个类,HistoryBook和MathBook,都继承自IBookService接口,注意他们的契约名都相同,都为MusicBook,后面再详细的说这个问题,修改后的program的代码如下:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection;
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    namespace MEFDemo
    {
      class Program
      {
         [ImportMany("MusicBook")]
        public IEnumerable<IBookService> Services { get; set; }
        static void Main(string[] args)
        {
          Program pro = new Program();
          pro.Compose();
          if (pro.Services != null)
          {
            foreach (var s in pro.Services)
            {
              Console.WriteLine(s.GetBookName());
            }
          }
          Console.Read();
        }
        private void Compose()
        {
          var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
          CompositionContainer container = new CompositionContainer(catalog);
          container.ComposeParts(this);
        }
      }
    }
    这里需要注意的是标红的两行代码,[ImportMany("MusicBook")]还有下面的声明变成了IEnumerable<>,因为要导出多个实例,所以要用到集合,下面采用foreach遍历输出,运行的结果如下图:
    一共三个,都输出了,对吧!是不是很好用啊,哈哈~~
    当然,如果想全部输出,可以向第一篇文章中那样,导入和导出时都不写契约名,就会全部导出。那么写契约名有什么好处呢?
    下面我们用代码说明问题,修改实现类的契约名如下:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel.Composition;
    namespace MEFDemo
    {
      [Export("MusicBook",typeof(IBookService))]
      public class MusicBook : IBookService
      {
        public string BookName { get; set; }
        public string GetBookName()
        {
          return "MusicBook";
        }
      }
      [Export("MathBook", typeof(IBookService))]
      public class MathBook : IBookService
      {
        public string BookName { get; set; }
        public string GetBookName()
        {
          return "MathBook";
        }
      }
      [Export("HistoryBook", typeof(IBookService))]
      public class HistoryBook : IBookService
      {
        public string BookName { get; set; }
        public string GetBookName()
        {
          return "HistoryBook";
        }
      }
    }
    现在三个类的契约名都不相同了,其他的代码不动,再次运行程序看看,是不是现在只输出MusicBook了,同理,修改[Import("Name")]中的契约名称,就会导入指定含有名称的类,契约名可以重复,这一以来,我们就可以用契约名给类进行分类,导入时可以根据契约名来导入。
    注意 : IEnumerable<T>中的类型必须和类的导出类型匹配,如类上面标注的是[Exprot(typeof(object))],那么就必须声明为IEnumerable<object>才能匹配到导出的类。
    例如:我们在类上面标注[Export("Book")],我们仅仅指定了契约名,而没有指定类型,那么默认为object,此时还用IEnumerable<IBookService>就匹配不到。
    那么,这种情况就要在输出是进行强制类型转换,代码如下:
    [Export("MusicBook")]
      public class MusicBook : IBookService
      {
        public string BookName { get; set; }
        public string GetBookName()
        {
          return "MusicBook";
        }
      }
    program中的代码改变如下:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection;
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    namespace MEFDemo
    {
      class Program
      {
        [ImportMany("MusicBook")]
        public IEnumerable<object> Services { get; set; }
        static void Main(string[] args)
        {
          Program pro = new Program();
          pro.Compose();
          if (pro.Services != null)
          {
            foreach (var s in pro.Services)
            {
              var ss = (IBookService)s;
              Console.WriteLine(ss.GetBookName());
            }
          }
          Console.Read();
        }
        private void Compose()
        {
          var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
          CompositionContainer container = new CompositionContainer(catalog);
          container.ComposeParts(this);
        }
      }
    }

    属性、方法、字段等都是可以注入的  

    如有疑问,请加群568055323联系群主。

  • 相关阅读:
    Optimization Landscape and Expressivity of DeepCNNs
    Requests从入门到进阶
    互联网架构师学习笔记整理
    使用Psutil监控系统资源
    Mac平台AirtestIDE adb 连接不上Genmotion Android9虚拟机的问题
    Mac版PyCharm,WebStrom闪退的处理办法 LSOpenURLsWithRole() failed with error -10810 for the file /Applications/Web
    Django Admin Cookbook-42如何在Django Admin后台控制台中设置应用程序和模型的顺序
    Django Admin Cookbook-41如何将数据库视图添加到Django Admin后台
    Django Admin Cookbook-40如何为Django Admin覆盖保存操作
    Django Admin Cookbook-39如何两次向Django管理员添加模型
  • 原文地址:https://www.cnblogs.com/xietianjiao/p/9239708.html
Copyright © 2011-2022 走看看