简要介绍 My.Ioc 的用法
下面这段代码展示了 My.Ioc 的基本用法:

1 using System; 2 using System.Collections.Generic; 3 4 namespace My.Ioc.Sample 5 { 6 public interface IConcurrency 7 { 8 int Code { get; } 9 } 10 11 public interface IConcurrencyService 12 { 13 string Name { get; } 14 void AddConcurrency(IConcurrency concurrency); 15 void RemoveConcurrency(IConcurrency concurrency); 16 } 17 18 public class ConcurrencyService : IConcurrencyService 19 { 20 readonly Dictionary<int, IConcurrency> _concurrencies = new Dictionary<int, IConcurrency>(); 21 22 public string Name 23 { 24 get { return GetType().Name; } 25 } 26 27 public void AddConcurrency(IConcurrency concurrency) 28 { 29 _concurrencies.Add(concurrency.Code, concurrency); 30 } 31 32 public void RemoveConcurrency(IConcurrency concurrency) 33 { 34 _concurrencies.Remove(concurrency.Code); 35 } 36 } 37 38 public class NewConcurrencyService : IConcurrencyService 39 { 40 #region IConcurrencyService Members 41 42 public string Name 43 { 44 get { return GetType().Name; } 45 } 46 47 public void AddConcurrency(IConcurrency concurrency) 48 { 49 throw new NotImplementedException(); 50 } 51 52 public void RemoveConcurrency(IConcurrency concurrency) 53 { 54 throw new NotImplementedException(); 55 } 56 57 #endregion 58 } 59 60 public interface ISimpleConsumer 61 { 62 IConcurrencyService ConcurrencyService { get; } 63 } 64 65 public class SimpleConsumer : ISimpleConsumer 66 { 67 readonly IConcurrencyService _concurrencyService; 68 69 public SimpleConsumer(IConcurrencyService concurrencyService) 70 { 71 _concurrencyService = concurrencyService; 72 } 73 74 public IConcurrencyService ConcurrencyService 75 { 76 get { return _concurrencyService; } 77 } 78 } 79 80 public interface IComplexConsumer : IDisposable 81 { 82 IConcurrencyService ConcurrencyService { get; } 83 } 84 85 public class ComplexConsumer : IComplexConsumer 86 { 87 readonly string _name; 88 readonly IConcurrencyService _concurrencyService; 89 90 public ComplexConsumer(string name, IConcurrencyService concurrencyService) 91 { 92 _name = name; 93 _concurrencyService = concurrencyService; 94 } 95 96 public IConcurrencyService ConcurrencyService 97 { 98 get { return _concurrencyService; } 99 } 100 101 public string Name 102 { 103 get { return _name; } 104 } 105 106 public string Address { get; set; } 107 108 public void Print() 109 { 110 Console.WriteLine(_name + " who lives in " + (Address ?? "Fujian") + " is using the service " + _concurrencyService.Name); 111 } 112 113 public void Dispose() 114 { 115 Console.WriteLine("ComplexConsumer is disposing..."); 116 } 117 } 118 119 class Program 120 { 121 static IObjectRegistration _concurrencyServiceRegistration; 122 static IObjectObserver<ISimpleConsumer> _simpleConsumerObserver; 123 124 static void Main(string[] args) 125 { 126 // First, we need to create an instance of IObjectContainer. 127 IObjectContainer container = new ObjectContainer(true); 128 129 // Then, we register some services 130 container.Register<IConcurrencyService, ConcurrencyService>() 131 .WhenParentTypeIsAny(typeof(SimpleConsumer), typeof(ComplexConsumer)) 132 .In(Lifetime.Container()) 133 .Set("ConcurrencyService") 134 .Return(out _concurrencyServiceRegistration); 135 136 container.Register<ISimpleConsumer, SimpleConsumer>(); 137 138 var consumerName = Parameter.Positional("Johnny.Liu"); 139 container.Register<IComplexConsumer, ComplexConsumer>() 140 .WithConstructor(consumerName) 141 .WithPropertyValue("Address", "Fujian") 142 .WithMethod("Print") 143 .In(Lifetime.Transient()); 144 145 // Finally, don't forget to commit the registrations to the registry. 146 container.CommitRegistrations(); 147 148 // Now you can ask the container to build instances for you. 149 var simpleConsumer1 = container.Resolve<ISimpleConsumer>(); 150 151 if (!container.TryGetObserver(out _simpleConsumerObserver)) 152 throw new Exception(); 153 _simpleConsumerObserver.Changed += OnObjectBuilderChanged; 154 var simpleConsumer2 = container.Resolve(_simpleConsumerObserver); 155 156 using (var scope = container.BeginLifetimeScope()) 157 { 158 var complexConsumer = scope.Resolve<IComplexConsumer>(); 159 } 160 161 // At last, we will unregister the current concurrency service to let the other concurrency 162 // service implementations to have a chance to replace it. 163 container.Unregister(_concurrencyServiceRegistration); 164 container.Register(typeof(IConcurrencyService), typeof(NewConcurrencyService)); 165 // As we said, don't forget to commit the registrations to the registry. 166 container.CommitRegistrations(); 167 168 using (var scope = container.BeginLifetimeScope()) 169 { 170 var complexConsumer = scope.Resolve<IComplexConsumer>(); 171 } 172 173 Console.ReadLine(); 174 } 175 176 static void OnObjectBuilderChanged(ObjectBuilderChangedEventArgs args) 177 { 178 Console.WriteLine(args.ChangeMode); 179 } 180 } 181 }
用法比较简单,跟大家熟悉的大多数 Ioc 容器差不多。我们这里来逐句解释一下:
IObjectContainer container = new ObjectContainer(true);
这一句创建了一个 ObjectContainer 对象。构造参数 true 表示容器默认将采用 Emit(动态生成代码)方式来构建对象。
container.Register<IConcurrencyService, ConcurrencyService>() .WhenParentTypeIsAny(typeof(SimpleConsumer), typeof(ComplexConsumer)) .In(Lifetime.Container()) .Set("ConcurrencyService") .Return(out _concurrencyServiceRegistration);
Register 方法将一个 ConcurrencyService 实现绑定到 IConcurrencyService 契约。WhenParentTypeIsAny 方法指定该服务只能用于 SimpleConsumer 或 ComplexConsumer 类。因此,如果您稍后在解析时写上以下这句:
var concurrencyService = container.Resolve<IConcurrencyService>();
则会收到一个 InvalidObjectBuilderException 错误,因为我们在上面的配置中已经指定了 IConcurrencyService 这个服务只能在构造 SimpleConsumer 或 ComplexConsumer 实例时由相应的 ObjectBuilder 向容器请求。
In 方法指定该服务注册项的生命周期,这里使用的是 Container 生命周期,即注册项的生命周期将同容器一样长,而且每次向容器请求该服务时,返回的都是相同的实例。也就是说,这是一个单例对象。Set 方法表明附加一个元数据 ("ConcurrencyService") 到该服务注册项中。Return 方法返回一个 IObjectRegistration 对象。该对象可以作为一个存根,用于在不再需要该服务的时候注销该服务。同时,它也可用于解析服务对象(调用 container.Resolve(IObjectRegistration registration) 方法重载),而不必像调用 container.Resolve(Type contractType) 时一样每次都从注册表中检索服务。
var consumerName = Parameter.Positional("Johnny.Liu"); container.Register<IComplexConsumer, ComplexConsumer>() .WithConstructor(consumerName) .WithPropertyValue("Address", "Fujian") .WithMethod("Print") .In(Lifetime.Transient());
上面这句代码指定将 ComplexConsumer 绑定到 IComplexConsumer,同时我们注意到这里还提供了一个默认构造参数。在这里,这个构造参数是必需的,因为 ComplexConsumer 构造函数的签名是:
public ComplexConsumer(string name, IConcurrencyService concurrencyService)
我们看到这个构造函数的第一个参数是 string 类型,在 My.Ioc 中这种类型的参数是不可自动装配的 (non-autowirable),用户必须为不可自动装配的依赖项提供一个默认值(不可自动装配的类型包括:所有值类型 + string + Type)。
WithPropertyValue 方法和 WithMethod 方法告诉容器,在构建好 ComplexConsumer 对象后,立即将该对象的 Address 属性赋值为“Fujian”,并调用该对象的“Print”方法。
配置(注册)好所有服务之后,此时这些服务并没有添加到注册表,而是被缓存到一个 RegistrationCommitter 中,因此我们需要显式调用下面这句代码将所有注册项提交到注册表中:
container.CommitRegistrations();
经过上面的配置,我们终于可以让容器为我们构建对象实例了。
var simpleConsumer1 = container.Resolve<ISimpleConsumer>();
这句正是向容器请求返回一个实现 ISimpleConsumer 接口的对象。
除了直接向容器请求对象之外,我们还可以向容器请求返回一个 IObjectObserver/IObjectCollectionObserver 对象,并通过该对象来解析服务实例,如下所示:
if (!container.TryGetObserver(out _simpleConsumerObserver)) throw new Exception(); _simpleConsumerObserver.Changed += OnObjectBuilderChanged; var simpleConsumer2 = container.Resolve(_simpleConsumerObserver);
这样做的好处是,当该对象 (SimpleConsumer) 依赖的其他子对象(这里是 IConcurrencyService 对象)注册/注销/激活/停用时, Observer 对象将会收到通知(需要订阅该 Observer 对象的 Changed 事件)。此外,IObjectObserver 和 IObjectCollectionObserver 对象与 IObjectRegistration 对象一样,也可以直接用于解析服务对象,而不必每次都向注册表检索服务,从而可以提高性能。
IComplexConsumer 对象的解析与 ISimpleConsumer 有点不一样,我们看下面这段代码:
using (var scope = container.BeginLifetimeScope()) { var complexConsumer = scope.Resolve<IComplexConsumer>(); }
这里,我们首先向容器请求获取一个 ILifetimeScope 对象,然后使用该对象来解析 IComplexConsumer 对象。这是因为 IComplexConsumer 实现了 IDisposable 接口,这表明该对象在使用完之后需要清理资源。在 My.Ioc 框架中,解析任何实现了 IDisposable 接口的对象时都需要先申请一个 ILifetimeScope,因为对象资源的清理是通过 ILifetimeScope 来完成的。ILifetimeScope 类似于我们通常所说的“变量作用域”的概念,但它还实现了对象共享的功能,关于这个话题我们还会在以后的文章中加以介绍,这里不再赘言。
使用完毕之后,我们可以将不再需要的服务注册项注销,如下所示:
container.Unregister(_concurrencyServiceRegistration);
运行这句之后,_simpleConsumerObserver 将被停用,而无法再用于解析服务对象,因为其依赖的服务 (IConcurrencyService) 已被注销。同时 OnObjectBuilderChanged 方法也会收到相应通知。不仅 ISimpleConsumer,此时所有直接和间接依赖于 IConcurrencyService 的服务都将被停用,而无法用于解析服务对象。如果我们想要让这些服务再次恢复功能,可以再注册一个实现了 IConcurrencyService 契约的服务并将其注册到容器中,如下代码所示:
container.Register(typeof(IConcurrencyService), typeof(NewConcurrencyService));
container.CommitRegistrations();
这样,那些依赖于 IConcurrencyService 契约的服务都将恢复功能。我们来再次运行下面的代码看一看:
using (var scope = container.BeginLifetimeScope()) { var complexConsumer = scope.Resolve<IComplexConsumer>(); }
如无意外,运行上面这段代码,控制台将会输出:
"Johnny.Liu who lives in Fujian is using the service NewConcurrencyService" "ComplexConsumer is disposing..."
表明我们的新 NewConcurrencyService 服务已取代了原来的 ConcurrencyService 并正常工作,且 ComplexConsumer 对象在超出作用域后已被清理。
总结
上面,我们简单地介绍了 My.Ioc 的使用方法。由于篇幅的缘故,很多问题并未谈及,我们将在以后的文章中逐一向大家介绍。