MEF学习(类同与工厂模式)

摘抄自http://www.cnblogs.com/comsokey/p/MEF1.html

一、什么是MEF

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

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

二、MEF简单例子

1、例子一

a、导入引用 System.ComponentModel.Composition

b、定义接口

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

c、使用接口:使用Import标记需要导入属性DemoOneInterface DO;

    public class DemoOne
    {
        [Import]
        DemoOneInterface DO;

        public void Run()
        {
            DO.Send("DemoOne.Run");
        }
    }

d、创建插件类:插件需要使用Export标记,并声明导出类型

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

e、查看效果:空指针异常

 

f、通知MEF去寻找插件

    class Program
    {
        static void Main(string[] args)
        {
            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();
            Console.Read();
        }
    }
View Code

 

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
    {
        public void Send(string msg)
        {
            Console.WriteLine("DemoOneInherit1 send {0}", msg);
        }
    }

    [Export("2", typeof(DemoOneInterface))]
    public class DemoOneInherit2 : DemoOneInterface
    {
        public void Send(string msg)
        {
            Console.WriteLine("DemoOneInherit2 send {0}", msg);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            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();
            Console.Read();
        }
    }
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
    {
        public void Send(string msg)
        {
            Console.WriteLine("DemoOneInherit1 send {0}", msg);
        }
    }

    [Export(typeof(DemoOneInterface))]
    public class DemoOneInherit2 : DemoOneInterface
    {
        public void Send(string msg)
        {
            Console.WriteLine("DemoOneInherit2 send {0}", msg);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            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();
            Console.Read();
        }
    }
View Code

 

4、例子四

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

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

使用到Lazy,来进行延迟加载,并且获取插件标记的信息。

a、新增插件描述类

    public interface DemoOneInterfaceDepict
    {
        string Depict { get; }
    }

b、为插件定义描述

 [Export(typeof(DemoOneInterface))]
    [ExportMetadata("Depict", "1")]
    public class DemoOneInherit1 : DemoOneInterface
    {
        public void Send(string msg)
        {
            Console.WriteLine("DemoOneInherit1 send {0}", msg);
        }
    }

    [Export(typeof(DemoOneInterface))]
    [ExportMetadata("Depict", "2")]
    public class DemoOneInherit2 : DemoOneInterface
    {
        public void Send(string msg)
        {
            Console.WriteLine("DemoOneInherit2 send {0}", msg);
        }
    }
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";
        }
    }
View Code

 

三、简化调用

1、使用基类

    public class BaseClass
    {
        public BaseClass()
        {
            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(this);
        }
    }
View Code

 然后继承基类 public class DemoOne : BaseClass

2、使用扩展

每个类都要继承这个基类,如果这个类已经继承了一个类,那么就比较麻烦了。

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

扩展方法:

    public static class ObjectExe
    {
        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("Addin"));   //遍历运行目录下的Addin文件夹,查找所需的插件。

            var _container = new CompositionContainer(catalog);

            _container.ComposeParts(obj);

            return obj;
        }
    }
View Code

修改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";
        }
    }
View Code

 四:总结:

除了一些Lazy的延迟加载,就简单调用而言,也只是一个工厂模式而已,只不过反射对应的不是类名,而是特性的名称。所以使用也没有多大的必要。

 

posted @ 2016-12-06 17:40  江境纣州  阅读(46)  评论(0)    收藏  举报