【最簡單IOC容器實現】實現一個最簡單的IOC容器 深度理解依賴注入(Dependence Injection) MEF隨筆索引

前面DebugLZQ的兩篇博文:html

淺談IOC--說清楚IOC是什麼架構

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隨筆索引

相關文章
相關標籤/搜索