这个场景跟《手写Unity容器--第一层依赖注入》又不同,这里构造Student的时候,Student依赖于1个Teacher,Teacher又依赖于1个Computer,而Computer又依赖于Power
链式依赖

一、条件
1、容器--工厂
2、集合
3、反射
4、特性-相当于配置(为什么相当于配置呢?因为假设Teacher有多个构造函数,不知道构造哪一个,所以需要标记出来)

二、思路
1、注册类型:RegisterType<TFrom,TTo>(),把类型的完整类型名称当作key放入数据字典,把类型当作value放入数据字典。
2、获取实例:Resolve<T>(),根据完整类型名称从字典中取出类型
3、得到类型构造函数的参数类型,递归创建参数类型实例,递归:隐形的跳出条件,条件就是GetParameters结果为空,targetType拥有无参数构造函数
4、最后再创建类型实例

三、代码实现
1、IStudent接口

namespace SimplestUnity_nLayer.Interface
{
interface IStudent
{
/// <summary>
/// 学习
/// </summary>
void Study();
}
}

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、ITeacher接口

namespace SimplestUnity_nLayer
{
interface ITeacher
{
/// <summary>
/// 教学
/// </summary>
void Teach();
}
}

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

5、IComputer接口

namespace SimplestUnity_nLayer
{
interface IComputer
{
/// <summary>
/// 显示
/// </summary>
void Show();
}
}

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

7、IPower接口

namespace SimplestUnity_nLayer
{
interface IPower
{
/// <summary>
/// 充电
/// </summary>
void ChargeBattery();
}
}

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

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

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)) > )
{
//得到第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());
}
}
}

11、标记特性--配置

namespace SimplestUnity_nLayer
{
public class DavidInjectionConstructor:Attribute
{
}
}

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

13、运行效果

构建学生的时候先构建了电源,后构建了电脑,其次构建了老师,最后才构建出学生。

14、项目截图

3、手写Unity容器--第N层依赖注入的更多相关文章

  1. 2、手写Unity容器--第一层依赖注入

    这个场景跟<手写Unity容器--极致简陋版Unity容器>不同,这里构造AndroidPhone的时候,AndroidPhone依赖于1个IPad 1.IPhone接口 namespac ...

  2. 1、手写Unity容器--极致简陋版Unity容器

    模拟Unity容器实例化AndroidPhone 思路: 1.注册类型:把类型完整名称作为key添加到数据字典中,类型添加到数据字典的value中 2.获取实例:根据完整类型名称也就是key取出val ...

  3. Ioc 器管理的应用程序设计,前奏:容器属于哪里? 控制容器的反转和依赖注入模式

    Ioc 器管理的应用程序设计,前奏:容器属于哪里?   我将讨论一些我认为应该应用于“容器管理”应用程序设计的原则. 模式1:服务字典 字典或关联数组是我们在软件工程中学到的第一个构造. 很容易看到使 ...

  4. ASP.NET Core Web 应用程序系列(一)- 使用ASP.NET Core内置的IoC容器DI进行批量依赖注入(MVC当中应用)

    在正式进入主题之前我们来看下几个概念: 一.依赖倒置 依赖倒置是编程五大原则之一,即: 1.上层模块不应该依赖于下层模块,它们共同依赖于一个抽象. 2.抽象不能依赖于具体,具体依赖于抽象. 其中上层就 ...

  5. 通过laravel理解IoC(控制反转)容器和DI(依赖注入)

    原文地址: http://www.insp.top/learn-laravel-container ,转载务必保留来源,谢谢了! 容器,字面上理解就是装东西的东西.常见的变量.对象属性等都可以算是容器 ...

  6. PHP 在Swoole中使用双IoC容器实现无污染的依赖注入

    简介: 容器(container)技术(可以理解为全局的工厂方法), 已经是现代项目的标配. 基于容器, 可以进一步实现控制反转, 依赖注入. Laravel 的巨大成功就是构建在它非常强大的IoC容 ...

  7. 从零写Java Web框架——实现Ioc依赖注入

    大概思路 通过读取配置文件,获取框架要加载的包路径:base-package,类似于 Spring 配置文件中的: <context:component-scan base-package=&q ...

  8. 手写IOC容器

    IOC(控制翻转)是程序设计的一种思想,其本质就是上端对象不能直接依赖于下端对象,要是依赖的话就要通过抽象来依赖.这是什么意思呢?意思就是上端对象如BLL层中,需要调用下端对象的DAL层时不能直接调用 ...

  9. [IoC容器Unity]第三回:依赖注入

    1.引言 上节介绍了,Unity的Lifetime Managers生命周期,Unity具体实现依赖注入包含构造函数注入.属性注入.方法注入,所谓注入相当赋值,下面一个一个来介绍. 2.构造函数注入 ...

随机推荐

  1. Literature Review: Improving Image-Based Localization by Active Correspondence Search

    Abstract Input: A query image Source: A point cloud reconstruction of a large scene (有一百多万3D点) Resul ...

  2. Linux运维--11.手动部署Galera Cluster

    1.搭建galera集群 yum install epel-release yum install centos-release-openstack-stein #1.1 安装mariadb yum ...

  3. gcd手写代码及STL中的使用方法

    一.手写代码 inline int gcd(int x,int y){ if(y==0) return x; else return(gcd(y,x%y)); } 二.STL中的使用方法 注:在STL ...

  4. python打包生成可执行文件教程

    python打包生成可执行文件教程 安装环境: 1.打开命令行窗口,输入命令:pip install pyinstaller ,等待下载结束.示例: 2.下载结束后,测试是否成功安装,输入命令:pyi ...

  5. Python之write与writelines区别

    一.传入的参数类型要求不同: 1. file.write(str)需要传入一个字符串做为参数,否则会报错. write( "字符串") with open('20200222.tx ...

  6. 【redisson】分布式锁与数据库事务

    场景: 用户消耗积分兑换商品. user_point(用户积分): id point 1 2000 point_item(积分商品): id point num 101 200 10 传统的contr ...

  7. vim编辑超大文件

    进入大文件(12g,250w+ lines),vim,耐心等待 有两种方法编辑删除冗余字段 1.set number ,可以通过:+数字组合跳到指定行,输入命令   ":100,200d&q ...

  8. Notes writer Pro

    Notes writer Pro pen钢笔和Pencil铅笔发现没啥区别 笔记软件使用说明书: https://www.lanzous.com/i777i3c

  9. 面试再问ThreadLocal,别说你不会!

    ThreadLocal是什么  以前面试的时候问到ThreadLocal总是一脸懵逼,只知道有这个哥们,不了解他是用来做什么的,更不清楚他的原理了.表面上看他是和多线程,线程同步有关的一个工具类,但其 ...

  10. node模块化开发基本知识学习笔记

    传统非模块化开发缺点: 1.命名冲突 2.文件依赖 标准的模块化规范: 1.AMD-requirejs 2.CMD-seajs 服务器端模块化规范: 1.CommonJS-Node.js 模块化相关的 ...