在使用 Ioc 框架时,一般我们建议集中在一个称为 Composition Root(其含义请参见下面的小注)的位置来注册 (Register) 和解析 (Resolve) 服务。该做法的目的在于通过限制 Ioc 的使用场合,尽量减少应用程序本身对于 Ioc 框架的依赖。
这种模式固然能够很好地解耦应用程序和 Ioc 框架,使我们能够在需要的时候方便地更换 Ioc 框架,但它同时也带来了一个问题:难道我们一定要在程序启动时注册所有服务吗?有些服务并不一定会马上用到,有一些服务甚至可能不会用到。在这种情况下,如果仍然按照上述模式,在程序启动时将所有服务统统注册到容器中,结果无疑会减缓程序的启动速度,同时会加大内存消耗。
针对这个问题,我们在 My.Ioc 中给出了延迟注册的解决办法。它的原理其实很简单:当应用程序向 My.Ioc 容器请求某个服务时,如果此时该服务尚未注册,容器便会触发一个 ObjectBuilderRequested 事件,并在事件参数中提供所请求的契约类型 (Contract Type) 和注入点信息 (InjectionInfo),而订阅了该事件的处理程序 (Event Handler) 便可以在这个时候根据契约类型和注入点信息来决定是否向容器中注册相应的服务以满足应用程序需求。
用法很简单,示例代码如下:
using System; using System.Diagnostics; using My.Ioc; namespace LazyRegistration { public interface ILazyService { } public class LazyService : ILazyService { } class Program { static bool _eventHandled = false; static void Main(string[] args) { IObjectContainer container = new ObjectContainer(false); container.ObjectBuilderRequested += OnObjectBuilderRequested; var lazy = container.Resolve<ILazyService>(); Debug.Assert(lazy != null); Debug.Assert(lazy is LazyService); Console.WriteLine(_eventHandled); Console.ReadLine(); } static void OnObjectBuilderRequested(ObjectBuilderRequestedEventArgs args) { if (args.ContractType == typeof (ILazyService)) { args.Register<ILazyService, LazyService>(); _eventHandled = true; } } } }
小注:
Composition Root 的定义是“一个组合应用程序各个模块的位置,一个应用程序中应该仅有一个这样的位置,这个位置通常是应用程序的入口点。例如,对于控制台应用程序来说,这个位置就是 Main 函数;对于 ASP.NET MVC 应用程序来说,这个位置可能是 global.asax;对于 WPF 应用程序来说,这个位置是 Application.OnStartup 方法...”。
源码可从此处下载。