前面DebugLZQ的兩篇博文:html
IoC Container Benchmark - Performance comparison框架
在淺談IOC--說清楚IOC是什麼中,DebugLZQ介紹了什麼是到底什麼是IOC/DI,再複習一下,那麼到底什麼是IOC呢?ssh
就像Martin Flower所說的:依賴對象的得到過程被反轉了,即由以前是consumer主動去new,變成consumer向IOC容器去要,而後由IOC容器注入依賴對象。ide
這個回答很是的口語化,但IOC的思想確實就是這樣。post
而後IoC Container Benchmark - Performance comparison對各類現有的IOC容器的性能作了比較。性能
感興趣的博友能夠看下前面的兩篇博文,會有助於你理解這一篇。ui
-------------------------------------------------------------------------------------------this
下面咱們就來實現一個最簡單的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); } } }
程序運行以下:
至此,正文完結。
參考:
突然想起一句話,不知是在哪裏看到的了,大概意思是:不能用本身語言講清楚的東西---你沒掌握,技術這個東西不存在所謂的「只可意會不可言傳」。
但願對你有幫助,老鳥、大蝦、高手等繞過,輕拍~
Update:請關注後續博文:MEF隨筆索引