- 容器移除了组件需要去定位它的依赖对象和管理这些对象的生命周期的任务。
- 容器实现了在不影响组件的情况下改变依赖的实现方法。
- 容器可以通过模拟依赖组件而使得测试更加容易。
- 容器通过更容易的添加新组件到系统中增强了应用程序的可维护性。
注意:
在初始化过程中,一种类型可以注册另外的类型,就像视图和服务。注册后就可以向整个容器提供它们的依赖关系,也可以通过其他类型来访问它们。为了做到这一点,类型需要在模块的构造器中向容器注入。以下代码是Commanding QuickStart示例代码中OrderModule是如何注册一个类型的。
public class OrderModule : IModule { public void Initialize() { this.container.RegisterType<IOrdersRepository,OrdersRepository>(new ContainerControlledLifetimeManager()); ... } ... } |
取决于你所选取的容器,注册也可以在代码外通过配置文件来实现。例如,参加第4章"Modular Application Development."中的"Registering Modules using a Configuration File"
MEF使用了基于属性的方式向容器中注册类型。因此,向容器中添加类型注册变的非常简单:为类型添加一个[Export]属性,如下代码所示,
[Export(typeof(ILoggerFacade))] public class CallbackLogger: ILoggerFacade { } |
使用MEF注册的另一种方法是将一个特定实例注册到容器中,如示例Modularity for Silverlight with MEF QuickStart中的QuickStartBootstrapper中注册函数ConfigureContainer所实现的这样,如下:
protected override void ConfigureContainer() { base.ConfigureContainer(); // Because we created the CallbackLogger and it needs to // be used immediately,we compose it to satisfy any imports it has. this.Container.ComposeExportedValue<CallbackLogger>(this.callbackLogger); } |
类型注册之后,它可被作为一个依赖来解析或者注册。当一个类型被解析时,容器需要创建一个新的实例,并将依赖关系注入到新创建的对象中。
一般来讲,当一个类型被解析时,会发生以下三种情况之一:
- 如果这个类型还未被注册,容器将会抛出一个异常
以下代码是Commanding QuickStart实例中的两个视图OrdersEditorView和OrdersToolBar是如何被容器解析,并且放置到其对应的区域中去的。
public class OrderModule : IModule { public void Initialize() { this.container.RegisterType<IOrdersRepository,OrdersRepository>(new ContainerControlledLifetimeManager()); // Show the Orders Editor view in the shell‘s main region. this.regionManager.RegisterViewWithRegion("MainRegion",() => this.container.Resolve<OrdersEditorView>()); // Show the Orders Toolbar view in the shell‘s toolbar region. this.regionManager.RegisterViewWithRegion("GlobalCommandsRegion",() => this.container.Resolve<OrdersToolBar>()); } ... } |
OrdersEditorPresentationModel构造方法中包含以下依赖(IOrdersRepository和OrdersCommandProxy),当它们被解析时,它们就被注入了。
public OrdersEditorPresentationModel( IOrdersRepository ordersRepository,OrdersCommandProxy commandProxy ) { this.ordersRepository = ordersRepository; this.commandProxy = commandProxy; // Create dummy order data. this.PopulateOrders(); // Initialize a CollectionView for the underlying Orders collection. #if SILVERLIGHT this.Orders = new PagedCollectionView( _orders ); #else this.Orders = new ListCollectionView( _orders ); #endif // Track the current selection. this.Orders.CurrentChanged += SelectedOrderChanged; this.Orders.MoveCurrentTo(null); } |
protected override DependencyObject CreateShell() { return this.Container.GetExportedValue<Shell>(); } |
如实例Modularity for Silverlight with MEF QuickStart中的ModuleA,ILoggerFacade和IModuleTracker就是以如下代码的方式注入的。
[ImportingConstructor] public ModuleA(ILoggerFacade logger,IModuleTracker moduleTracker) { if (logger == null) { throw new ArgumentNullException("logger"); } if (moduleTracker == null) { throw new ArgumentNullException("moduleTracker"); } this.logger = logger; this.moduleTracker = moduleTracker; this.moduleTracker.RecordModuleConstructed(WellKnownModuleNames.ModuleA); } |
另一种方式是使用属性注入,如Modularity for Silverlight with MEF QuickStart实例中的ModuleTracker类中向实例中注入ILoggerFacade的所示方法。
[Export(typeof(IModuleTracker))] public class ModuleTracker : IModuleTracker { // Due to Silverlight/MEF restrictions,this must be public. [Import] public ILoggerFacade Logger; } |
Prism 类库提供了UnityServiceLocatorAdapter和MefServiceLocatorAdapter. 这两个类都实现了通过扩展ServiceLocatorImplBase实现了ISeviceLocator接口,下面插图展示了类之间的关系
虽然Prism类库没有依赖或者实现任何特定的容器,但通常情况下,应用程序会依赖于某个特定的容器。这意味特定的应用程序会依赖某个容器,但是Prism类库并不直接和任何容器发生关系的是合理的。例如Stock Trader RI和一部分的QuickStart使用Prism并且依赖于Unity容器,而另一部分QuickStart和示例则依赖于MEF。
IServiceLocator
public interface IServiceLocator : IServiceProvider { object GetInstance(Type serviceType); object GetInstance(Type serviceType,string key); IEnumerable<object> GetAllInstances(Type serviceType); TService GetInstance<TService>(); TService GetInstance<TService>(string key); IEnumerable<TService> GetAllInstances<TService>(); } |
public static class ServiceLocatorExtensions { public static object TryResolve(this IServiceLocator locator,Type type) { try { return locator.GetInstance(type); } catch (ActivationException) { return null; } } public static T TryResolve<T>(this IServiceLocator locator) where T: class { return locator.TryResolve(typeof(T)) as T; } } |
ModuleInitializer使用IServiceLocator在模块的加载过程中解析模块,如下代码所示:
IModule moduleInstance = null; try { moduleInstance = this.CreateModule(moduleInfo); moduleInstance.Initialize(); } ... |
protected virtual IModule CreateModule(string typeName) { Type moduleType = Type.GetType(typeName); if (moduleType == null) { throw new ModuleInitializeException(string.Format(CultureInfo.CurrentCulture,Properties.Resources.FailedToGetType,typeName)); } return (IModule)this.serviceLocator.GetInstance(moduleType); } |
- 你是一个独立软件开发商(ISV),开发一个适用于多容器的第三方服务
- 你在一个使用多种容器的系统中设计一个服务
- "Unity Application Block" on MSDN:
http://www.msdn.com/unity - Unity community site on CodePlex:
http://www.codeplex.com/unity - "Managed Extensibility Framework Overview" on MSDN:
http://msdn.microsoft.com/en-us/library/dd460648.aspx - MEF community site on CodePlex:
http://mef.codeplex.com/ - "Inversion of Control containers and the Dependency Injection pattern" on Martin Fowler‘s website:
http://www.martinfowler.com/articles/injection.html - "Design Patterns: Dependency Injection" inMSDN Magazine:
http://msdn.microsoft.com/en-us/magazine/cc163739.aspx - "Loosen Up: Tame Your Software Dependencies for More Flexible Apps" inMSDN Magazine:
http://msdn.microsoft.com/en-us/magazine/cc337885.aspx - Castle Project:
http://www.castleproject.org/container/index.html - StructureMap:
http://structuremap.sourceforge.net/Default.htm - Spring.NET:
http://www.springframework.net/