3、手写Unity容器--第N层依赖注入
这个场景跟《手写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层依赖注入的更多相关文章
- 2、手写Unity容器--第一层依赖注入
这个场景跟<手写Unity容器--极致简陋版Unity容器>不同,这里构造AndroidPhone的时候,AndroidPhone依赖于1个IPad 1.IPhone接口 namespac ...
- 1、手写Unity容器--极致简陋版Unity容器
模拟Unity容器实例化AndroidPhone 思路: 1.注册类型:把类型完整名称作为key添加到数据字典中,类型添加到数据字典的value中 2.获取实例:根据完整类型名称也就是key取出val ...
- Ioc 器管理的应用程序设计,前奏:容器属于哪里? 控制容器的反转和依赖注入模式
Ioc 器管理的应用程序设计,前奏:容器属于哪里? 我将讨论一些我认为应该应用于“容器管理”应用程序设计的原则. 模式1:服务字典 字典或关联数组是我们在软件工程中学到的第一个构造. 很容易看到使 ...
- ASP.NET Core Web 应用程序系列(一)- 使用ASP.NET Core内置的IoC容器DI进行批量依赖注入(MVC当中应用)
在正式进入主题之前我们来看下几个概念: 一.依赖倒置 依赖倒置是编程五大原则之一,即: 1.上层模块不应该依赖于下层模块,它们共同依赖于一个抽象. 2.抽象不能依赖于具体,具体依赖于抽象. 其中上层就 ...
- 通过laravel理解IoC(控制反转)容器和DI(依赖注入)
原文地址: http://www.insp.top/learn-laravel-container ,转载务必保留来源,谢谢了! 容器,字面上理解就是装东西的东西.常见的变量.对象属性等都可以算是容器 ...
- PHP 在Swoole中使用双IoC容器实现无污染的依赖注入
简介: 容器(container)技术(可以理解为全局的工厂方法), 已经是现代项目的标配. 基于容器, 可以进一步实现控制反转, 依赖注入. Laravel 的巨大成功就是构建在它非常强大的IoC容 ...
- 从零写Java Web框架——实现Ioc依赖注入
大概思路 通过读取配置文件,获取框架要加载的包路径:base-package,类似于 Spring 配置文件中的: <context:component-scan base-package=&q ...
- 手写IOC容器
IOC(控制翻转)是程序设计的一种思想,其本质就是上端对象不能直接依赖于下端对象,要是依赖的话就要通过抽象来依赖.这是什么意思呢?意思就是上端对象如BLL层中,需要调用下端对象的DAL层时不能直接调用 ...
- [IoC容器Unity]第三回:依赖注入
1.引言 上节介绍了,Unity的Lifetime Managers生命周期,Unity具体实现依赖注入包含构造函数注入.属性注入.方法注入,所谓注入相当赋值,下面一个一个来介绍. 2.构造函数注入 ...
随机推荐
- asp.net core 3.x 授权默认流程
一.前言 接上一篇<asp.net core 3.x 授权中的概念>,本篇看看asp.net core默认授权的流程.从两个方面来看整个授权系统是怎么运行的:启动阶段的配置.请求阶段中间件 ...
- Go语言基础之结构体(面向对象编程上)
1 自定义类型和类型别名 1.1 自定义类型 Go语言中可以基于一些基本的数据类型,使用type关键字定义自定义类型的数据 . 自定义类型定义了一个全新的类型,该新类型具有基本数据类型的特性.自定义类 ...
- node准备
=== 原生的api === express express 中间件相关的. https://juejin.im/post/5aa345116fb9a028e52d7217 推荐几篇入门的优质博客: ...
- Python常用库 - os库
os简单介绍 os 模块提供了非常丰富的方法用来处理文件和目录 os关于目录路径的方法 # 获取当前路径 path = os.getcwd() # 获取当前绝对路径 os.path.abspath(p ...
- python函数定义中引用外部变量的一个问题
如果在函数定义的默认值中引用了一个外部变量,如下所示 x = 3 def func(a = x): print(a, x) 那么a的默认值就会是3, 但是print语句中的x会是调用时的x值 lamb ...
- C# 数据类型详解以及变量、对象与内存
学习刘铁猛老师<C#语言入门详解>视频,针对其中重点知识点进行总结. 1.什么是类型? 类型又称为数据类型(Data Type),数据类型在数据结构中的定义是一个值的集合以及定义在这个值集 ...
- 「Flink」使用Managed Keyed State实现计数窗口功能
先上代码: public class WordCountKeyedState { public static void main(String[] args) throws Exception { S ...
- Linux分区工具-parted
parted用于操纵磁盘分区的程序,通常用于规则大小超过2T的分区,也可用于小分区的规划:它支持多种分区表格式,包括MS-DOS(MBR)和GPT:这对于为新操作系统创建空间,重新组织磁盘使用以及将数 ...
- springboot~HttpPut开启application/x-www-form-urlencoded
在使用spring框架时,默认情况下@RequestParam注解只到接受Get和Post请求参数 ,而对于Put来说默认是使用@ReqeustBody注解的,如果希望为Put也开启@RequestP ...
- 清北学堂—2020.1提高储备营—Day 4 morning(数论)
qbxt Day 4 morning --2020.1.20 济南 主讲:李奥 目录一览 1.一些符号与基本知识 2.拓展欧几里得,逆元与欧拉定理 3.线性筛法与积性函数(非重点) 总知识点:数论 一 ...