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