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(); } }
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(); } }
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(); } }
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); } }
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"; } }
三、简化调用
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); } }
然后继承基类 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; } }
修改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"; } }
四:总结:
除了一些Lazy的延迟加载,就简单调用而言,也只是一个工厂模式而已,只不过反射对应的不是类名,而是特性的名称。所以使用也没有多大的必要。

浙公网安备 33010602011771号