.Net下实现可扩展的编程方法简述
IoC控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则用来解决系统耦合问题。 控制反转还有一个名字叫做依赖注入(DI:Dependency Injection)。IoC中最基本的技术就是interface + reflection,“反射(Reflection)”编程。 。" IoC 容器的职责是对象的创建,核心是对象的生命周期管理(何时创建/怎么创建/何时销毁),就像一个大工厂,对象“托管”在里面。模式的源头都是 SOLID(面向对象的原则)。利用IOC 解耦的核心是:面向抽象,而非实现。即我们只需要关注某一个对象能做什么,怎么用;不需要关注它怎么来,更不需要我们来管理它。
1,Unity
Unity是Enterprise Library 4.0开始加入的一个轻量级的、可扩展的依赖注入容器。
Unity模块包括了下面的特点
• 提供了一个创建对象,以及依赖的对象的方法
• 提供的RegisterType方法用来在容器中注册类型和映射,Resolve方法可以返回任何依赖对象的实例。
• 提供控制反转IOC功能,通过预先配置注入类的对象来实现。你可以在构造函数中指明一个类或者接口(构造函数注入),或者是使用attribute的属性注入,和方法调用注入。
• 支持容器继承,容器可以有子容器,支持对象从子容器传递到父容器中。
• 可以从标准的配置文件中读取信息,例如xml文件
• 在运行时可以配置和改变依赖关系。
• 对类的定义没有任何要求。在类上不需要添加attribute(除非使用属性注入或者是方法调用注入),在类声明中没有任何限制。
• 支持自定义容器,例如,你可以在方法中实现额外的对象构造,和容器功能,例如容器的缓存功能。
三种注入方式:
•构造器注入(Constructor Injection):IoC容器会智能地选择选择和调用适合的构造函数以创建依赖的对象。如果被选择的构造函数具有相应的参数,IoC容器在调用构造函数之前会自定义创建相应参数对象;
•属性注入(Property Injection):如果需要使用到被依赖对象的某个属性,在被依赖对象被创建之后,IoC容器会自动初始化该属性;
• 方法注入(Method Injection):如果被依赖对象需要调用某个方法进行相应的初始化,在该对象创建之后,IoC容器会自动调用该方法。
定义一个接口:
实现接口用来扩展应用程序:
public class FlatFileLogger : ILogger { public void Write(string message) { Console.WriteLine(String.Format("Message:{0}", message)); Console.WriteLine("Target:FlatFile"); } }
主程序:
class Program { static void Main(string[] args) { ILogger logger = GetLogger(); logger.Write("Test"); System.Console.ReadLine(); } /// <summary> /// 加载日志处理对象 /// </summary> /// <returns></returns> public static ILogger GetLogger() { //初始化一个容器 IUnityContainer container = new UnityContainer(); //获取unity配置 UnityConfigurationSection config = ConfigurationManager.GetSection("unity") as UnityConfigurationSection; UnityConfigurationSection.CurrentSection.Configure(container); //向容器中注册对象 ILogger defaultLogger = container.Resolve<ILogger>("Logger"); return defaultLogger; } }
扩展配置:
<configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/> </configSections> <unity> <typeAliases> <typeAlias alias="FlatFileLogger" type="IoCTest.Unity.FlatFileLogger, IoCTest.Unity" /> <typeAlias alias="ILogger" type="IoCTest.Unity.ILogger, IoCTest.Unity" /> </typeAliases> <containers> <container> <types> <type name="Logger" type="ILogger" mapTo="FlatFileLogger" /> </types> </container> </containers> </unity> </configuration>
2,Spring.NET
Spring.NET是一个应用程序框架,其目的是协助开发人员创建企业级的.NET应用程序。它提供了很多方面的功能,比如依赖注入、面向方面编程(AOP)、数据访问抽象及ASP.NET扩展等等。 Spring.NET的IoC容器所解决的,正是如何在企业应用中将类、对象和服务组合成应用程序的问题。IoC容器将分散的组件组合成完整的应用程序。 。Spring.Core的基础是IObjectFactory接口,该接口用一个简单而优雅的方式实现了工厂模式,使我们可以无需自行编写singleton类型和众多的服务定位器,并允许将对象配置及其依赖关系与具体的程序逻辑解耦。该模块中的IApplicationContext接口是IObjectFactory的扩展,增加了诸多企业级功能,包括使用资源文件进行文本本地化、事件传播和资源装载等等。
定义一个接口:
实现接口:
public class FileLogger : ILogger { public void Write(string message) { Console.WriteLine(String.Format("Message:{0}", message)); Console.WriteLine("Target:File"); } }
主程序:
static void Main(string[] args) { //初始化容器 IApplicationContext context = ContextRegistry.GetContext(); //获取容器中的对象 ILogger Logger = context.GetObject("Logger") as ILogger; Logger.Write("Test"); Console.ReadLine(); }
系统配置:
<configuration> <configSections> <sectionGroup name="spring"> <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/> <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /> </sectionGroup> </configSections> <spring> <context> <resource uri="config://spring/objects"/> </context> <objects xmlns="http://www.springframework.net"> <object id="Logger" type="IoCTest.Spring.FileLogger,IoCTest.Spring" > </object> </objects> </spring> </configuration>
3、MEF组合部件
MEF(Managed Extensibility Framework ) 已经内置在.NET Framework 4.0里面了,只需要添加引用System.ComponentModel.Composition即可。 是.NET平台下一个提供轻量级的、可扩展的、类似插件式的系统架构的、且无需配置的(Attribute Based)扩展性管理框架,让我们可以轻松的对应用程序进行扩展并且对已有的代码产生最小的影响,开发人员在开发过程中根据功能要求定义一些扩展点,之后扩展人员就可以使用这些扩展点与应用程序交互;同时MEF让应用程序与扩展程序之间不产生直接的依赖,这样也允许在多个具有同样的扩展需求之间共享扩展程序。虽然微软的人极力否认MEF是一个IoC/DI的工具,但事实是它的确可以实现Ioc/DI。
使用 MEF 编写的可扩展应用程序的扩展点都会声明一个可由扩展组件(Parts)填充的导入(Import),每个扩展组件都会声明一个导出(Export)。MEF提供一种通过“组合”隐式发现部件的方法,对应用程序进行扩展。 所谓导入(Import)即:在容器中指定“哪个对象需要Compose”,所谓导出(Export)即:指定“哪个类可以被用来Compose”
契约接口:
声明导出:
/// <summary> /// 哪个类可以被用来Compose,也就是说这个类是不是可以用来填充的实现类,所以Export标记的是类,而不是具体的某个对象。 /// </summary> [Export("ILogger1", typeof(ILogger))] public class FileLogger1 : ILogger { public void Write(string message) { Console.WriteLine(String.Format("Message:{0}", message)); Console.WriteLine("Target:File"); } }
声明导入:
class Ts { /// <summary> /// 哪个对象需要Compose。也就是需要被实现类给填充,所以Import标记的是对象,一般该对象是接口,因为如果是具体类的话,那还需要Import吗? /// </summary> [Import("ILogger1")] public ILogger Logger { get; set; } public Ts() { var catalog = new AggregateCatalog(); //把从Program所在程序集中发现的部件添加到目录中 //catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly)); //把从指定path发现的部件添加到目录中 catalog.Catalogs.Add(new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory)); var container = new CompositionContainer(catalog); //通过调用容器的ComposeParts()方法可以把容器中的部件组合到一起。 container.ComposeParts(this); } }
主程序:
相对于Spring.net这样的框架来说,MEF的优势就是首先它是.NET Framework内置的,你无需添加第三方的引用,担心第三方组件的更新等问题;其次它是免配置的,对Spring.net这样的庞然大物来说免配置很有诱惑力。对Unity来说,它的优势是一样的,.NET Framework内置,无需配置,无需hard code的声明。当然更无需直接引用了,这是所有IoC都做到的。
MEF还可以通过metadata自动发现Parts而无需获取parts的assembly/dll。有关MEF的完整的介绍,请移步MSDN:http://msdn.microsoft.com/en-us/library/dd460648.aspx