• 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(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
        }

     运行效果:

    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
        }

    运行效果:

    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
        }

    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。

  • 相关阅读:
    枚举类 --单例模式
    模板设计模式
    动态代理
    反射应用--修改属性值
    通过反射绕过泛型
    java反射
    网络编程练习
    TCP编程
    GUI 聊天界面
    UDP传输多线程
  • 原文地址:https://www.cnblogs.com/mapstar/p/16598651.html
Copyright © 2020-2023  润新知