zoukankan      html  css  js  c++  java
  • 【最简单IOC容器实现】实现一个最简单的IOC容器

    前面DebugLZQ的两篇博文:

    浅谈IOC--说清楚IOC是什么

    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);
            }
        }
    }

    程序运行如下:

    至此,正文完结。

    参考:

    忽然想起一句话,不知是在哪里看到的了,大概意思是:不能用自己语言讲清楚的东西---你没掌握,技术这个东西不存在所谓的“只可意会不可言传”。

    希望对你有帮助,老鸟、大虾、高手等绕过,轻拍~

    Update:请关注后续博文:MEF随笔索引

  • 相关阅读:
    字符统计(改1)
    结对作业—电梯调度
    第三次作业(字符,单词统计)
    第三周作业二(读程序)
    Visual Studio 2015的安装及单元测试练习
    四则运算改进版
    简单的四则运算(续)
    第二次作业—————暴风影音点评(修改版)
    软件工程学习总结篇
    电梯调度问题之成长总结篇(二)
  • 原文地址:https://www.cnblogs.com/DebugLZQ/p/3114832.html
Copyright © 2011-2022 走看看