zoukankan      html  css  js  c++  java
  • MEF学习

    一、   什么是MEF

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

      我的理解:应用/插件均使用约定好的协议(接口)进行开发。系统将自动扫描指定文件夹,并按协议自动导入。

    二、   MEF简单例子

    1、例子一

    a、定义接口

     public interface DemoOneInterface   
        {
            void Send(string msg);
        }

    b、使用接口

        public class DemoOne
        {
            [Import]
            DemoOneInterface DO;
    
            public void Run()
            {
                DO.Send("DemoOne.Run");
            }
        }
    使用[Import]标记需要导入属性(DemoOneInterface DO;),如果不标记,则MEF不会进行导入。

    c、创建插件类

        [Export(typeof(DemoOneInterface))]
        public class DemoOneInherit1 : DemoOneInterface
        {
    
            #region DemoOneInterface Members
    
            public void Send(string msg)
            {
                Console.WriteLine("DemoOneInherit1 send {0}", msg);
            }
    
            #endregion
        }
    插件

     插件类需要使用Export标记,并且声称导出类型。 

    d、查看效果

      static void Main(string[] args)
            {
                new DemoOne().Run();
    
                Console.ReadLine();
            }

    原来我们使用MEF,但并没有通知MEF去寻找插件。

    我们对Main函数进行修改:

    var demo = new DemoOne();
    
                var catalog = new AggregateCatalog();
    
                catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
    
                //catalog.Catalogs.Add(new DirectoryCatalog("Addin"));   //遍历运行目录下的Addin文件夹,查找所需的插件。
    
                var _container = new CompositionContainer(catalog);
    
                _container.ComposeParts(demo);
                
                demo.Run();

    修改后再次运行看看效果。

    OK,运行起来了,和预期一样。

    2、例子二

    运行例子一,没有问题,但2个插件使用同一个的时候,会报错。

    因此我们可以为Export加入别名(contractName),并且Import的时候也指定别名,MEF就会根据别名自动进行加载。

    修改后代码如下:

    public class DemoOne
        {
            [Import("2")]
            DemoOneInterface DO;
    
            public void Run()
            {
                DO.Send("DemoOne.Run");
            }
        }
    
        public interface DemoOneInterface   
        {
            void Send(string msg);
        }
    
        [Export("1",typeof(DemoOneInterface))]
        public class DemoOneInherit1 : DemoOneInterface
        {
    
            #region DemoOneInterface Members
    
            public void Send(string msg)
            {
                Console.WriteLine("DemoOneInherit1 send {0}", msg);
            }
    
            #endregion
        }
    
    
    
        [Export("2", typeof(DemoOneInterface))]
        public class DemoOneInherit12 : DemoOneInterface
        {
    
            #region DemoOneInterface Members
    
            public void Send(string msg)
            {
                Console.WriteLine("DemoOneInherit2 send {0}", msg);
            }
    
            #endregion
        }
    View Code

     运行效果:

    3、例子三

    有时我们希望一个同时使用多个插件,比如:输出log。

    这时我们可以将Import改为ImportMany,并且修改Do的类型为IEnumerable<DemoOneInterface>来导入多个插件。

    修改后代码:

       public class DemoOne
        {
            [ImportMany]
            IEnumerable<DemoOneInterface> DoList;
    
            public void Run()
            {
                foreach (var _do in DoList)
                {
                    _do.Send("DemoOne.Run");
                }
            }
        }
    
        public interface DemoOneInterface   
        {
            void Send(string msg);
        }
    
        [Export(typeof(DemoOneInterface))]
        public class DemoOneInherit1 : DemoOneInterface
        {
    
            #region DemoOneInterface Members
    
            public void Send(string msg)
            {
                Console.WriteLine("DemoOneInherit1 send {0}", msg);
            }
    
            #endregion
        }
    
    
    
        [Export(typeof(DemoOneInterface))]
        public class DemoOneInherit12 : DemoOneInterface
        {
    
            #region DemoOneInterface Members
    
            public void Send(string msg)
            {
                Console.WriteLine("DemoOneInherit2 send {0}", msg);
            }
    
            #endregion
        }
    View Code

    运行效果:

    4、例子四

    现在有很多插件使用同一个约定,但我想根据配置在同一个方法中调用某个插件。

    这时我们需要使用ExportMetadata来为插件的特殊属性进行标记。

    使用到Lazy,来进行延迟加载,并且获取插件标记的信息。(关于Lazy具体信息请自行查找)

    a、新增插件描述类

        public interface DemoOneInterfaceDepict
        {
            string Depict{get;}
        }

    b、为插件定义描述

     [Export(typeof(DemoOneInterface))]
        [ExportMetadata("Depict", "1")]
        public class DemoOneInherit1 : DemoOneInterface
        {
    
            #region DemoOneInterface Members
    
            public void Send(string msg)
            {
                Console.WriteLine("DemoOneInherit1 send {0}", msg);
            }
    
            #endregion
        }
    
    
    
        [Export(typeof(DemoOneInterface))]
        [ExportMetadata("Depict", "2")]
        public class DemoOneInherit12 : DemoOneInterface
        {
    
            #region DemoOneInterface Members
    
            public void Send(string msg)
            {
                Console.WriteLine("DemoOneInherit2 send {0}", msg);
            }
    
            #endregion
        }
    View Code

    c、修改DoList

    IEnumerable<Lazy<DemoOneInterface,DemoOneInterfaceDepict>> DoList;

    d、根据配置调用 

     public class DemoOne
        {
            [ImportMany]
            IEnumerable<Lazy<DemoOneInterface,DemoOneInterfaceDepict>> DoList;
    
            public void Run()
            {
                foreach (var _do in DoList.Where(item=>item.Metadata.Depict == ReadXml()))
                {
                    _do.Value.Send("DemoOne.Run");
                }
            }
    
            string ReadXml()
            {
                return "2";
            }
        }

    运行结果:

    三、简化调用

    上述4个例子运行正常,但我们一直没去在意Main函数里面的内容。

     var demo = new DemoOne();
    
                var catalog = new AggregateCatalog();
    
                catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
    
                //catalog.Catalogs.Add(new DirectoryCatalog("Addin"));   //遍历运行目录下的Addin文件夹,查找所需的插件。
    
                var _container = new CompositionContainer(catalog);
    
                _container.ComposeParts(demo);
                
                demo.Run();

    看着头就晕了,难道每次构造一个函数,都这么写吗?那不是非常痛苦?!!!

    重新设计一下:

    1、使用基类

        public abstract class BaseClass
        {
            public BaseClass()
            {
                var catalog = new AggregateCatalog();
    
                catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
    
                var _container = new CompositionContainer(catalog);
    
                _container.ComposeParts(this);
            }
        }

    修改DemoOne类继承BaseClass

     public class DemoOne : BaseClass

    简化调用

     var demo = new DemoOne();
     demo.Run();

    运行 ok。

    2、使用扩展方法

    每个类都要继承这个基类,由于C#只有单继承,已经继承了一个基类后,就比较麻烦。

    因此衍生出第二种方法,新增扩展方法。

    扩展方法

    public static class ObjectExt
        {
            public static T ComposePartsSelf<T>(this T obj) where T : class
            {
                var catalog = new AggregateCatalog();
    
                catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
                catalog.Catalogs.Add(new DirectoryCatalog("."));
                //catalog.Catalogs.Add(new DirectoryCatalog("addin"));
    
                var _container = new CompositionContainer(catalog);
    
                _container.ComposeParts(obj);
    
                return obj;
            }
        }

    修改DemoOne类,新增构造函数,并且调用扩展方法

      public class DemoOne 
        {
            public DemoOne()
            {
                this.ComposePartsSelf();
            }
    
            [ImportMany]
            IEnumerable<Lazy<DemoOneInterface,DemoOneInterfaceDepict>> DoList;
    
            public void Run()
            {
                foreach (var _do in DoList.Where(item=>item.Metadata.Depict == ReadXml()))
                {
                    _do.Value.Send("DemoOne.Run");
                }
            }
    
            string ReadXml()
            {
                return "2";
            }
        }

    简化调用

     var demo = new DemoOne();
     demo.Run();

    运行 ok。

  • 相关阅读:
    ruby 二进制转十进制 Integer("0b101") = 5
    开始菜单和我的文档的我的图片及我的音乐变成 my pictrues 正常图标了
    ruby watir 莫名其妙的错误
    Excel SaveAS是去掉提示框
    apache && jboss安装
    ruby require include的区别
    ruby控制鼠标
    This error is raised because the column 'type' is reserved for storing the class in case of inheritance
    用正则表达式限制文本框只能输入数字,小数点,英文字母,汉字等各类代码
    ASP.NET 如何动态修改 Header 属性如添加 Meta 标签 keywords description!
  • 原文地址:https://www.cnblogs.com/comsokey/p/MEF1.html
Copyright © 2011-2022 走看看