【IOC】3.IOC-手写Unity容器-链式依赖-第N层构造函数注入
这个场景跟《手写Unity容器--第一层依赖注入》又不同,这里构造Student的时候,Student依赖于1个Teacher,Teacher又依赖于1个Computer,而Computer又依赖于Power
学生学习依赖于老师,老师教学依赖于电脑,电脑工作依赖于电源,这种链式依赖的关系
一.条件
1.1.容器-造对象的工厂
1.2.集合-存储类型的集合,因为有了类型才能反射创建对象
1.3.反射
1.4.特性-相当于配置(注:因为一个类里面有那么多属性,那么多方法,那么多构造函数,不是每一个都需要注入,所以这里特性就是个标记,标记哪些属性,方法,构造函数需要注入)
二.思路
2.1.注册类型:RegisterType<TFrom,TTo>(),把类型的完整类型名称当作key放入数据字典,把类型当作value放入数据字典。
2.2.获取实例:Resolve<T>(),根据完整类型名称从字典中取出类型
2.3.得到类型构造函数的参数类型,递归创建参数类型实例,递归:隐形的跳出条件,条件就是GetParameters结果为空,targetType拥有无参数构造函数
2.4.最后再创建类型实例
三.代码落地
3.1.IStudent接口
namespace SimplestUnity_nLayer.Interface { interface IStudent { /// <summary> /// 学习 /// </summary> void Study(); } }
3.2.Students接口实现
namespace SimplestUnity_nLayer { class Student:IStudent { [DavidInjectionConstructor] public Student(ITeacher iTeacher) { Console.WriteLine("{0}构造函数", this.GetType().Name); } /// <summary> /// 学习 /// </summary> public void Study() { Console.WriteLine("{0}学习", this.GetType().Name); } } }
3.3.ITeacher接口
namespace SimplestUnity_nLayer { interface ITeacher { /// <summary> /// 教学 /// </summary> void Teach(); } }
3.4.Teacher实现
namespace SimplestUnity_nLayer { class Teacher:ITeacher { [DavidInjectionConstructor] public Teacher(IComputer iComputer) { Console.WriteLine("{0}构造函数", this.GetType().Name); } /// <summary> /// 教学 /// </summary> public void Teach() { Console.WriteLine("{0}教学", this.GetType().Name); } } }
3.5.IComputer接口
namespace SimplestUnity_nLayer { interface IComputer { /// <summary> /// 显示 /// </summary> void Show(); } }
3.6.Computer实现
namespace SimplestUnity_nLayer { class Computer: IComputer { [DavidInjectionConstructor] public Computer(IPower iPower) { Console.WriteLine("{0}构造函数", this.GetType().Name); } /// <summary> /// 显示 /// </summary> public void Show() { Console.WriteLine("{0}显示", this.GetType().Name); } } }
3.7.IPower接口
namespace SimplestUnity_nLayer { interface IPower { /// <summary> /// 充电 /// </summary> void ChargeBattery(); } }
3.8.Power实现
namespace SimplestUnity_nLayer { public class Power : IPower { [DavidInjectionConstructor] public Power() { Console.WriteLine("{0}构造函数", this.GetType().Name); } /// <summary> /// 充电 /// </summary> public void ChargeBattery() { Console.WriteLine("充电中{0}", this.GetType().Name); } } }
3.9.容器-接口
namespace SimplestUnity_nLayer { public interface IDaivdContainer { /// <summary> /// 注册类型 /// </summary> /// <typeparam name="TFrom"></typeparam> /// <typeparam name="TTo"></typeparam> void RegisterType<TFrom, TTo>(); /// <summary> /// 获取实例 /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> T Resolve<T>(); } }
3.10、容器-实现
namespace SimplestUnity_nLayer { /// <summary> /// 容器--工厂 /// </summary> public class DaivdContainer:IDaivdContainer { private Dictionary<string, Type> containerDictionary = new Dictionary<string, Type>();//字典 /// <summary> /// 注册类型 /// </summary> /// <typeparam name="TFrom"></typeparam> /// <typeparam name="TTo"></typeparam> public void RegisterType<TFrom, TTo>() { containerDictionary.Add(typeof(TFrom).FullName, typeof(TTo)); } /// <summary> /// 获取实例 /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> public T Resolve<T>() { Type type = containerDictionary[typeof(T).FullName]; return (T)this.CreateInstance(type); } private object CreateInstance(Type type) { //1、得到类型的所有构造函数 ConstructorInfo[] ctorArray = type.GetConstructors(); //2、得到有标记DavidInjectionConstructor特性的构造函数,如果都没有标记特性,那么得到参数最多的构造函数 ConstructorInfo currentCtor = null; if (ctorArray.Count(c => c.IsDefined(typeof(DavidInjectionConstructor), true)) > 0) { //得到第1个标记DavidInjectionConstructor特性的构造函数 currentCtor = ctorArray.FirstOrDefault(c => c.IsDefined(typeof(DavidInjectionConstructor), true)); } else { //得到参数个数最多的构造函数 currentCtor = ctorArray.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault(); } List<object> paraList = new List<object>(); //递归:隐形的跳出条件,条件就是GetParameters结果为空,targetType拥有无参数构造函数 foreach (var para in currentCtor.GetParameters()) { //得到的参数类型是IPower,抽象无法创建实例 var paraType = para.ParameterType; //所以根据IPower Key,得到Power类型,具体类型就可以创建实例 var targetParaType = containerDictionary[paraType.FullName]; //继续检查targetParaType的构造函数,不能直接创建实例了 Object obj = this.CreateInstance(targetParaType); paraList.Add(obj); } return Activator.CreateInstance(type, paraList.ToArray()); } } }
3.11.标记特性-配置
namespace SimplestUnity_nLayer { public class DavidInjectionConstructor:Attribute { } }
3.12.客户端调用
using SimplestUnity_nLayer.Interface; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace SimplestUnity_nLayer { class Program { static void Main(string[] args) { //传统做法 { Power power = new Power(); Computer computer = new Computer(power); Teacher teacher = new Teacher(computer); Student student = new Student(teacher); student.Study(); } //容器做法 { DaivdContainer davidContainer = new DaivdContainer(); davidContainer.RegisterType<IStudent, Student>(); davidContainer.RegisterType<ITeacher, Teacher>(); davidContainer.RegisterType<IComputer, Computer>(); davidContainer.RegisterType<IPower, Power>(); IStudent iStudent = davidContainer.Resolve<IStudent>(); iStudent.Study(); } } } }
3.13.运行效果
构造学生的时候先构造了电源,后构造了电脑,其次构造了老师,最后才构造出学生。
3.14.项目截图
旧书不厌百回读,熟读深思子自知。