这个场景跟《手写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. 简单ts文件结构

    一.ts文件结构 DEMO{ 1. .vscode:特有文件夹,调试的配置文件,启动浏览器 2. Js:放ts编译后的文件,不管 3. Ts:放ts文件,敲代码 4. tsconfig.json:ts ...

  2. Pyinstaller打包exe,丢失图标等问题

    Pyinstaller打包exe,丢失图标等问题 一.原因 exe运行时会解压一个名为'_MEI*'的资源文件夹到电脑的临时目录,程序结束时删除. 程序里使用'\图标.png'这样的路径,exe运行时 ...

  3. clr via c# 程序集加载和反射(2)

    查看,clr via c# 程序集加载和反射(1) 8,发现类型的成员: 字段,构造器,方法,属性,事件,嵌套类型都可以作为类型成员.其包含在抽象类MemberInfo中,封装了所有类型都有的一组属性 ...

  4. dotnetcore3.1 WPF 实现多语言

    dotnetcore3.1 WPF 实现多语言 Intro 最近把 DbTool 从 WinForm 迁移到了 WPF,并更新到了 dotnet core 3.1,并实现了基于 Microsoft.E ...

  5. SharePoint PowerShell SendEmail

    前言 最近碰到这样一个需求,用户需要个简单的定时邮件提醒,就是抓取SharePoint某个列表里的值,然后作为邮件地址/邮件主题/邮件内容发送出去. 自己想了想,既然用户要求每天定时发送,那么肯定是任 ...

  6. 【mysql】索引相关的个人总结

    重点参考: MySQL索引原理及慢查询优化 (美团技术分享网站):原理.示例优化都写的很好. 索引很难么?带你从头到尾捋一遍MySQL索引结构,不信你学不会!:原理写的很好. [从入门到入土]令人脱发 ...

  7. 在本地搭建git服务器

    GitHub就是一个免费托管开源代码的远程仓库.但是对于某些视源代码如生命的商业公司来说,既不想公开源代码,又舍不得给GitHub交保护费,那就只能自己搭建一台Git服务器作为私有仓库使用. 搭建Gi ...

  8. 使用BeanUtils.populate将map集合封装为bean对象

    1.前言 最近在做一个javaweb项目练练手,涉及到把jsp页面中表单的内容存到数据库,和request.getParameterMap配合使用可以将jsp页面表单的数据转化为bean对象. 2.介 ...

  9. MRAM技术进入汽车应用

    在整个地址空间范围内读写各种类型的数据.通常MRAM的操作和时序类似于32位微控制器的规范和时序.与DLFASH相比,当今的非易失性存储器可以接受MRAM设备的性能和吞吐量. 与当今的DFLASH相比 ...

  10. 个性化和云端孤岛困扰SaaS用户,低代码PaaS或成解决之道 ZT

    近日,中国软件行业协会.中国软件网联合阿里云推出了<2020中国SaaS产业十大趋势>,其中明确指出企业软件SaaS化是大势所趋,但个性化和云端孤岛成为2020年SaaS用户关注的两大问题 ...