【最简单IOC容器实现】实现一个最简单的IOC容器
前面DebugLZQ的两篇博文:
IoC Container Benchmark - Performance comparison
在浅谈IOC--说清楚IOC是什么中,DebugLZQ介绍了什么是到底什么是IOC/DI,再复习一下,那么到底什么是IOC呢?
就像Martin Flower所说的:依赖对象的获得过程被反转了,即由之前是consumer主动去new,变成consumer向IOC容器去要,然后由IOC容器注入依赖对象。
这个回答非常的口语化,但IOC的思想确实就是这样。
然后IoC Container Benchmark - Performance comparison对各种现有的IOC容器的性能做了比较。
感兴趣的博友可以看下前面的两篇博文,会有助于你理解这一篇。
-------------------------------------------------------------------------------------------
下面我们就来实现一个最简单的IOC容器。
我们要做的事情是:从一个地方获得输入,然后将这个输入再Write出去。
作为对比,首先来看一下不用IOC,即consumer主动去new的情况:
using System; namespace Normal { public interface IReader { string Read(); } public interface IWriter { void Write(string data); } public class ReadKeyboard : IReader { public string Read() { return "code to read from keyboard and return as string"; } } public class ReadScanner : IReader { public string Read() { return "code to read from scanner and return as string"; } } public class WritePrinter : IWriter { public void Write(string data) { Console.WriteLine("code to write to the printer: "+data); } } public class WriteFlashDisk : IWriter { public void Write(string data) { Console.WriteLine("code to write to the flash disk: " + data); } } public class Copy { public void DoWork(IReader reader,IWriter writer) { string data = reader.Read(); writer.Write(data); } } class Test { static void Main(string[] args) { Copy copy = new Copy(); copy.DoWork(new ReadKeyboard(),new WritePrinter());//依赖对象获得时多个依赖,应当用IOCContainer解耦 } } }
撇去Test类不看,以上代码堪称clean,完美符合SOLID(FxCop、StyleCop等默认rules就不谈了)。
Test类依然是"new对象",这是问题的所在。
---------------------------------------------------------------
进入正题:实现这个最简单的IOC容器!
为上面的代码添加一个自定义的IOC容器类,其核心思想是由反射去构建依赖对象。
这个IOC容器的代码如下:
/// <summary> /// My own IocContainer /// http://www.cnblogs.com/DebugLZQ /// </summary> public class Container { private Dictionary<Type, Type> iocMap = new Dictionary<Type, Type>(); public void Register<TypetoResolve, ResolvedType>() { if (iocMap.ContainsKey(typeof(TypetoResolve))) { throw new Exception(string.Format("Type {0} already registered.", typeof(TypetoResolve).FullName)); } iocMap.Add(typeof(TypetoResolve), typeof(ResolvedType)); } public T Resolve<T>() { return (T)Resolve(typeof(T)); } public object Resolve(Type typeToResolve) { //Find the registered type for typeToResolve if (!iocMap.ContainsKey(typeToResolve)) { throw new Exception(string.Format("Can't resolve type {0}. Type is not registered.", typeToResolve.FullName)); } Type resolvedType = iocMap[typeToResolve]; //Try to construct the object //step-1: find the constructor. ConstructorInfo ctorInfo = resolvedType.GetConstructors().First(); //step-2:find the parameters for the constructor and try to resolve those. List<ParameterInfo> paramsInfo = ctorInfo.GetParameters().ToList(); List<object> resolvedParams = new List<object>(); foreach (ParameterInfo param in paramsInfo) { Type t = param.ParameterType; object res = Resolve(t); resolvedParams.Add(res); } //step-3:using reflection invoke constructor to create the object object retObject = ctorInfo.Invoke(resolvedParams.ToArray()); return retObject; } }
--------------------------------------------------------------------
其他部分保持不变(Test类除外)。
如何使用这个IOC容器呢?
给出完整代码,如下:
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace ContainerDI2_Test { public interface IReader { string Read(); } public interface IWriter { void Write(string data); } public class ReadKeyboard : IReader { public string Read() { return "code to read from keyboard and return as string"; } } public class ReadScanner : IReader { public string Read() { return "code to read from scanner and return as string"; } } public class WritePrinter : IWriter { public void Write(string data) { Console.WriteLine("code to write to the printer: " + data); } } public class WriteFlashDisk : IWriter { public void Write(string data) { Console.WriteLine("code to write to the flash disk: " + data); } } public class Copy { public void DoWork(IReader reader, IWriter writer) { string data = reader.Read(); writer.Write(data); } } /// <summary> /// My own IocContainer /// http://www.cnblogs.com/DebugLZQ /// </summary> public class Container { private Dictionary<Type, Type> iocMap = new Dictionary<Type, Type>(); public void Register<TypetoResolve, ResolvedType>() { if (iocMap.ContainsKey(typeof(TypetoResolve))) { throw new Exception(string.Format("Type {0} already registered.", typeof(TypetoResolve).FullName)); } iocMap.Add(typeof(TypetoResolve), typeof(ResolvedType)); } public T Resolve<T>() { return (T)Resolve(typeof(T)); } public object Resolve(Type typeToResolve) { //Find the registered type for typeToResolve if (!iocMap.ContainsKey(typeToResolve)) { throw new Exception(string.Format("Can't resolve type {0}. Type is not registered.", typeToResolve.FullName)); } Type resolvedType = iocMap[typeToResolve]; //Try to construct the object //step-1: find the constructor. ConstructorInfo ctorInfo = resolvedType.GetConstructors().First(); //step-2:find the parameters for the constructor and try to resolve those. List<ParameterInfo> paramsInfo = ctorInfo.GetParameters().ToList(); List<object> resolvedParams = new List<object>(); foreach (ParameterInfo param in paramsInfo) { Type t = param.ParameterType; object res = Resolve(t); resolvedParams.Add(res); } //step-3:using reflection invoke constructor to create the object object retObject = ctorInfo.Invoke(resolvedParams.ToArray()); return retObject; } } class Test { static void DIRegistration(Container container) { container.Register<IReader, ReadKeyboard>(); container.Register<IWriter, WritePrinter>(); } static void Main(string[] args) { Container container = new Container(); DIRegistration(container); Copy copy = new Copy(); copy.DoWork(container.Resolve<IReader>(), container.Resolve<IWriter>());//依赖于IOC容器了 } } }
可以和前面new那个代码比较下,看看两者的区别。
-----------------------------------------------------------------
当然DebugLZQ为了更清楚的说明代码问题,因而没有进行分层,当然你也可以把工程写成这样:
分层的好处是结构清晰。
以上两段代码,均可正常运行,运行结果相同,其运行截图如下:
--------------------------------------------------------------------------------
当然根据个人编码习惯,我们也可以把Copy类写成这样
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace ContainerDI { public interface IReader { string Read(); } public interface IWriter { void Write(string data); } public class ReadKeyboard : IReader { public string Read() { return "code to read from keyboard and return as string"; } } public class ReadScanner : IReader { public string Read() { return "code to read from scanner and return as string"; } } public class WritePrinter : IWriter { public void Write(string data) { Console.WriteLine("code to write to the printer: " + data); } } public class WriteFlashDisk : IWriter { public void Write(string data) { Console.WriteLine("code to write to the flash disk: " + data); } } public class Copy { private Container _container; private IReader _reader; private IWriter _writer;//也可以是具体类型 public Copy(Container container) { this._container = container; this._reader = _container.Resolve<IReader>(); this._writer = _container.Resolve<IWriter>(); } public void DoWork() { string data = _reader.Read(); _writer.Write(data); } } /// <summary> /// My own IocContainer /// </summary> public class Container { private Dictionary<Type, Type> iocMap = new Dictionary<Type, Type>(); public void Register<TypetoResolve, ResolvedType>() { if (iocMap.ContainsKey(typeof(TypetoResolve))) { throw new Exception(string.Format("Type {0} already registered.",typeof(TypetoResolve).FullName)); } iocMap.Add(typeof(TypetoResolve), typeof(ResolvedType)); } public T Resolve<T>() { return (T)Resolve(typeof(T)); } public object Resolve(Type typeToResolve) { //Find the registered type for typeToResolve if(!iocMap.ContainsKey(typeToResolve)) { throw new Exception(string.Format("Can't resolve type {0}. Type is not registered.",typeToResolve.FullName)); } Type resolvedType = iocMap[typeToResolve]; //Try to construct the object //step-1: find the constructor. ConstructorInfo ctorInfo = resolvedType.GetConstructors().First(); //step-2:find the parameters for the constructor and try to resolve those. List<ParameterInfo> paramsInfo = ctorInfo.GetParameters().ToList(); List<object> resolvedParams = new List<object>(); foreach (ParameterInfo param in paramsInfo) { Type t = param.ParameterType; object res = Resolve(t); resolvedParams.Add(res); } //step-3:using reflection invoke constructor to create the object object retObject = ctorInfo.Invoke(resolvedParams.ToArray()); return retObject; } } class Test { static void DIRegistration(Container container) { container.Register<IReader, ReadKeyboard>(); container.Register<IWriter, WritePrinter>(); } static void Main(string[] args) { Container container = new Container(); DIRegistration(container); Copy copy = new Copy(container); copy.DoWork(); } } }
也可以写成这样:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; namespace ContainerDI2 { public interface IReader { string Read(); } public interface IWriter { void Write(string data); } public class ReadKeyboard : IReader { public string Read() { return "code to read from keyboard and return as string"; } } public class ReadScanner : IReader { public string Read() { return "code to read from scanner and return as string"; } } public class WritePrinter : IWriter { public void Write(string data) { Console.WriteLine("code to write to the printer: " + data); } } public class WriteFlashDisk : IWriter { public void Write(string data) { Console.WriteLine("code to write to the flash disk: " + data); } } public class Copy { private IReader _reader; private IWriter _writer;//也可以是具体类型 public Copy(IReader reader,IWriter writer) { this._reader = reader; this._writer = writer; } public void DoWork() { string data = _reader.Read(); _writer.Write(data); } } /// <summary> /// My own IocContainer /// http://www.cnblogs.com/DebugLZQ /// </summary> public class Container { private Dictionary<Type, Type> iocMap = new Dictionary<Type, Type>(); public void Register<TypetoResolve, ResolvedType>() { if (iocMap.ContainsKey(typeof(TypetoResolve))) { throw new Exception(string.Format("Type {0} already registered.", typeof(TypetoResolve).FullName)); } iocMap.Add(typeof(TypetoResolve), typeof(ResolvedType)); } public T Resolve<T>() { return (T)Resolve(typeof(T)); } public object Resolve(Type typeToResolve) { //Find the registered type for typeToResolve if (!iocMap.ContainsKey(typeToResolve)) { throw new Exception(string.Format("Can't resolve type {0}. Type is not registered.", typeToResolve.FullName)); } Type resolvedType = iocMap[typeToResolve]; //Try to construct the object //step-1: find the constructor. ConstructorInfo ctorInfo = resolvedType.GetConstructors().First(); //step-2:find the parameters for the constructor and try to resolve those. List<ParameterInfo> paramsInfo = ctorInfo.GetParameters().ToList(); List<object> resolvedParams = new List<object>(); foreach (ParameterInfo param in paramsInfo) { Type t = param.ParameterType; object res = Resolve(t); resolvedParams.Add(res); } //step-3:using reflection invoke constructor to create the object object retObject = ctorInfo.Invoke(resolvedParams.ToArray()); return retObject; } } class Test { static void DIRegistration(Container container) { container.Register<IReader, ReadKeyboard>(); container.Register<IWriter, WritePrinter>(); } static void Main(string[] args) { Container container = new Container(); DIRegistration(container); Copy copy = new Copy(container.Resolve<IReader>(),container.Resolve<IWriter>()); copy.DoWork(); } } }
程序均可正常工作,运行结果与前面相同。
至此,最简单的IOC容器完成。
----------------------------------------------------------------------
-----------------------------------------------------------------------
上面的IOC容器,每次都重新构建一个新的对象。
我们稍加改造:
如果我们需要维持对象对象的某个状态(如对象的创建时刻等),也就是说给给IOC容器构建的对象加上生命周期。那我们该怎么办呢?
思路很简单:对于需要维持状态的对象,IOC第一次构建以后,先存着,后来有要用的直接返回前面构建的那个。
把我们的思路转换成代码,如下:
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace ContainerDIWithLifeOption { public interface IReader { string Read(); } public interface IWriter { void Write(string data); } public class ReadKeyboard : IReader { public string Read() { return "code to read from keyboard and return as string"; } } public class ReadScanner : IReader { public string Read() { return "code to read from scanner and return as string"; } } public class WritePrinter : IWriter { public void Write(string data) { Console.WriteLine("code to write to the printer: " + data); } } public class WriteFlashDisk : IWriter { public void Write(string data) { Console.WriteLine("code to write to the flash disk: " + data); } } public class Copy { private IReader _reader; private IWriter _writer;//也可以是具体类型 public Copy(IReader reader, IWriter writer) { this._reader = reader; this._writer = writer; } public void DoWork() { string data = _reader.Read(); _writer.Write(data); } } public enum LifeTimeOptions { TransientLifeTimeOption, ContainerControlledLifeTimeOption } public class ResolvedTypeWithLifeTimeOptions { public Type ResolvedType { get; set; } public LifeTimeOptions LifeTimeOption { get; set; } public object InstanceValue { get; set; } public ResolvedTypeWithLifeTimeOptions(Type resolvedType) { ResolvedType = resolvedType; LifeTimeOption = LifeTimeOptions.TransientLifeTimeOption; InstanceValue = null; } public ResolvedTypeWithLifeTimeOptions(Type resolvedType, LifeTimeOptions lifeTimeOption) { ResolvedType = resolvedType; LifeTimeOption = lifeTimeOption; InstanceValue = null; } } public class Container { private Dictionary<Type, ResolvedTypeWithLifeTimeOptions> iocMap = new Dictionary<Type, ResolvedTypeWithLifeTimeOptions>(); public void Register<T1, T2>() { Register<T1, T2>(LifeTimeOptions.TransientLifeTimeOption); } public void Register<T1, T2>(LifeTimeOptions lifeTimeOption) { if (iocMap.ContainsKey(typeof (T1))) { throw new Exception(string.Format("Type {0} already registered.", typeof (T1).FullName)); } ResolvedTypeWithLifeTimeOptions targetType = new ResolvedTypeWithLifeTimeOptions(typeof (T2), lifeTimeOption); iocMap.Add(typeof (T1), targetType); } public T Resolve<T>() { return (T) Resolve(typeof (T)); } public object Resolve(Type typeToResolve) { // Find the registered type for typeToResolve if (!iocMap.ContainsKey(typeToResolve)) throw new Exception(string.Format("Can't resolve {0}.Type is not registered.", typeToResolve.FullName)); ResolvedTypeWithLifeTimeOptions resolvedType = iocMap[typeToResolve]; // Step-1: If LifeTimeOption is ContainerControlled and there is //already an instance created then return the created instance. if (resolvedType.LifeTimeOption == LifeTimeOptions.ContainerControlledLifeTimeOption&& resolvedType.InstanceValue != null) return resolvedType.InstanceValue; // Try to construct the object // Step-2: find the constructor //(ideally first constructor if multiple constructors present for the type) ConstructorInfo ctorInfo = resolvedType.ResolvedType.GetConstructors().First(); // Step-3: find the parameters for the constructor and try to resolve those List<ParameterInfo> paramsInfo = ctorInfo.GetParameters().ToList(); List<object> resolvedParams = new List<object>(); foreach (ParameterInfo param in paramsInfo) { Type t = param.ParameterType; object res = Resolve(t); resolvedParams.Add(res); } // Step-4: using reflection invoke constructor to create the object object retObject = ctorInfo.Invoke(resolvedParams.ToArray()); resolvedType.InstanceValue = retObject; return retObject; } } class Program { static void Main(string[] args) { Container container = new Container(); DIRegistration(container); Copy copy = new Copy(container.Resolve<IReader>(),container.Resolve<IWriter>()); copy.DoWork(); Console.ReadLine(); Copy copy2 = new Copy(container.Resolve<IReader>(), container.Resolve<IWriter>()); copy2.DoWork(); Console.ReadLine(); } private static void DIRegistration(Container container) { container.Register<IReader, ReadKeyboard>(LifeTimeOptions.ContainerControlledLifeTimeOption); container.Register<IWriter, WritePrinter>(LifeTimeOptions.TransientLifeTimeOption); } } }
程序运行如下:
至此,正文完结。
参考:
- CSDN: 架构师之路(39)---IoC框架
- EagleFish(邢瑜琨) 深度理解依赖注入(Dependence Injection)
- Martin Flower Inversion of Control Containers and the Dependency Injection pattern
- 黄忠成 Object Builder Application Block
- Dependency Inversion Principle, IoC Container & Dependency Injection (Part - 1)
- Dependency Inversion Principle, IoC Container & Dependency Injection (Part - 2)
- Dependency Inversion Principle, IoC Container, and Dependency Injection (Part - 3)
- Dependency Inversion Principle, IoC Container, and Dependency Injection (Part - 4)
- http://www.codeproject.com/Articles/538536/A-curry-of-Dependency-Inversion-Principle-DIP-Inve
- http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html
- http://www.pluralsight.com/training
- http://www.oodesign.com
- http://blog.architexa.com/2010/05/types-of-dependency-injection/
- http://www.codeproject.com/Articles/495019/Dependency-Inversion-Principle-and-the-Dependency
- and so on
忽然想起一句话,不知是在哪里看到的了,大概意思是:不能用自己语言讲清楚的东西---你没掌握,技术这个东西不存在所谓的“只可意会不可言传”。
希望对你有帮助,老鸟、大虾、高手等绕过,轻拍~
Update:请关注后续博文:MEF随笔索引