IOC详解

概念

IOC控制反转

为什么叫控制反转/反转控制?

   这里分为两个词语理解,控制即创建、管理的能力,反转即为将权力(创建、管理)的能力交给外部。

IOC和依赖注入(DI)有什么关系?

  IOC可以理解为一种设计思想,而依赖注入则是IOC的具体体现,打个比方将军在地图上排兵布阵,士兵在阵地上作战就是对将军排兵布阵的具体体现。

 IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。

 

依赖注入的好处

   我们先来说说传统模式下的缺点,比如传统开发中有两个类,类A和类B,如果要使用类B的对象,那么我们需要通过new创建类B的实例,才能在类A里使用类B的对象。这样

会导致类的依赖性变强,如果类B发生变化类A也可能需要跟着变化。

再来说说依赖注入,将创建对象的控制权交给外部(诸如spring.net ......),通过容器来实例化对象,需要哪个对象就直接从容器拿就行,从而降低类之间的耦合(或者说是依赖性)

资源变的容易管理

 

DI依赖注入

           依赖注入就是能做到构造某个对象时,将依赖的对象自动初始化并注入

三种注入方式:构造函数注入--属性注入--工厂方法注入(按时间顺序)

构造函数注入用的最多,默认找参数最多的构造函数,可以不用特性,可以去掉对容器的依赖。不管是手写容器或者是使用Spring.net等等框架来实现依赖注入,底层都会运用到

反射,这就导致性能上会有所损耗,但是一般情况可以忽略不计。

 

总结

           IOC是目标是效果(或者叫设计理念),IOC 最常见以及最合理的实现方式叫做依赖注入(Dependency Injection,简称 DI)。

并且Martin Fowler(创作者)在一篇文章中提到将 IoC 改名为 DI,原文如下,原文地址:
https://martinfowler.com/articles/injection.html

 

 

 

 

手写IOC(工厂方法简易)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace John.Framework
{
    /// <summary>
    /// 工厂类 创建构造对象
    /// </summary>
    public class JohnContainer
    {
        //此处不用静态的是因为多个注册需要每次都不一样
        private  Dictionary<string, Type> JohnContainerDictionary = new Dictionary<string, Type>();

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TFrom">要构造对象的依赖对象,
        /// 比如类A实现了接口B,TFrom就是接口B</typeparam>
        /// 
        /// <typeparam name="Tto">要构造的对象</typeparam>
        public  void RegisterType<TFrom, Tto>()
        {

            //这里使用FullName是需要写完整名称,防止重复
            JohnContainerDictionary.Add(typeof(TFrom).FullName,typeof(Tto));



        }

        public  T Resolve<T>()
        {

            Type type = JohnContainerDictionary[typeof(T).FullName];
            return (T)this.CreateObject(type);
        }

        /// <summary>
        /// 只能构造无参的
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T ResoleNoPar<T>()
        {
            // //通过反射获取到类型名称,  这里是通过键的形式从字典里获取
            Type type = JohnContainerDictionary[typeof(T).FullName];
            return (T)Activator.CreateInstance(type);  //创建实例并返回   

        }



        /// <summary>
        /// 带参数 只有一层的构造
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T ResoleOnePar<T>()
        {
            Type type = JohnContainerDictionary[typeof(T).FullName];
            //默认会找参数最多的构造函数
            //但是此处还有问题,比如有多个构造方法,而且两个参数都一样,怎么处理?
            //此时就可以用特性标记需要的构造函数

            //找出所有的构造函数
            ConstructorInfo[] ctorArry = type.GetConstructors();
            ConstructorInfo ctor = null;
            //判断标记特性的构造函数
            if (ctorArry.Count(
                 c => c.IsDefined(
                  typeof(JohnInjectionConstructorAttribute), true)) > 0)
            {
                //如果有标记特性的,找出来
                ctor = ctorArry.FirstOrDefault(c => c.IsDefined(typeof(JohnInjectionConstructorAttribute), true));

            }
            else
            {
                //如果没有,找出构造方法里参数个数最长的(按降序排,找第一个)
                ctor = ctorArry.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
            }

            //例如:类A依赖类接口B ,类A带参数,参数C也是一个类,依赖注入就需要先构造类C,再构造类A
            List<object> list = new List<object>();
            foreach (var parameter in ctor.GetParameters())//找出所有的参数
            {
                Type paratype = parameter.ParameterType;//得到参数类型
                                                        //参数不能直接构造,从字典里拿出参数的完整名称
                Type targetType = JohnContainerDictionary[paratype.FullName];

                list.Add(Activator.CreateInstance(targetType));

            }

            return (T)Activator.CreateInstance(type, list.ToArray());
        }


        /// <summary>
        /// 带参数 无限层依赖的构造
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private object CreateObject(Type type)
        {

            //找出所有的构造函数
            ConstructorInfo[] ctorArry = type.GetConstructors();
            ConstructorInfo ctor = null;
            //判断标记特性的构造函数
            if (ctorArry.Count(
                 c => c.IsDefined(
                  typeof(JohnInjectionConstructorAttribute), true)) > 0)
            {
                //如果有标记特性的,找出来
                ctor = ctorArry.FirstOrDefault(c => c.IsDefined(typeof(JohnInjectionConstructorAttribute), true));
            }
            else
            {
                //如果没有,找出构造方法里参数个数最长的(按降序排,找第一个)
                ctor = ctorArry.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
            }

            //例如:类A依赖类接口B ,类A带参数,参数C也是一个类它的构造方法中又有参数D
            List<object> list = new List<object>();
            foreach (var parameter in ctor.GetParameters())//找出所有的参数
            {
                Type paratype = parameter.ParameterType;//得到参数类型
                                                        //参数不能直接构造,从字典里拿出参数的完整名称
                Type targetType = JohnContainerDictionary[paratype.FullName];
                object para = this.CreateObject(targetType);
                //递归:隐形的跳出条件,就是GetParameters结果为空,targetType拥有无参数构造函数
                list.Add(para);
            }

            return Activator.CreateInstance(type, list.ToArray());
        }


    }

    }
}

 

posted @ 2019-11-25 21:32  唐什么来着  阅读(134)  评论(0编辑  收藏  举报