重新整理.net core 计1400篇[六] (.net core 一个简易版的依赖注入容器 )
前言
我们了解到一个依赖注入的形式是:
注入依赖服务:var root = new Cat().Register<IFoo, Foo>(Lifetime.Transient);
获取对应的实例:
GetServices(cat1);
那么这个是如何实现的呢?
看第一个new Cat()这时候做了什么?
public class Cat : IServiceProvider, IDisposable
{
internal readonly Cat _root;
internal readonly ConcurrentDictionary<Type, ServiceRegistry> _registries;
private readonly ConcurrentDictionary<Key, object> _services;
private readonly ConcurrentBag<IDisposable> _disposables;
private volatile bool _disposed;
public Cat()
{
	_registries = new ConcurrentDictionary<Type, ServiceRegistry>();
	_root = this;
	_services = new ConcurrentDictionary<Key, object>();
	_disposables = new ConcurrentBag<IDisposable>();
}
这里可能有一些人没有接触过ConcurrentDictionary和 ConcurrentBag,
这两个分别是线性安全的dictionary和list。是的,因为我们的ioc可能在不同线程中处理对象,那么这个时候是需要lock的,但是lock效率并不高,具体怎么实现的可以去看源码。
现在我们知道在cat创建的时候呢,会创建一个注册字典,一个服务字典,一个垃圾回收list。
下面就是去注册服务:
public static Cat Register<TFrom, TTo>(this Cat cat, Lifetime lifetime) where TTo : TFrom
            => cat.Register(typeof(TFrom), typeof(TTo), lifetime);
具体的实现:
public static Cat Register(this Cat cat, Type from, Type to, Lifetime lifetime)
        {
            Func<Cat, Type[], object> factory = (_, arguments) => Create(_, to, arguments);
            cat.Register(new ServiceRegistry(from, lifetime, factory));
            return cat;
        }
解释一下过程:创建一个用来工厂去生产to。
new ServiceRegistry(from, lifetime, factory)
将服务注册对象中中,封装from,生命周期,和生产to的工厂。
这里面的作用:
列如GetServices(cat1);,那么其获取到Foo的步骤为,通过IFOO找到对应的注册服务,然后通过注册服务去生产Foo。
关注一下cat.Register(new ServiceRegistry(from, lifetime, factory));:
public Cat Register(ServiceRegistry registry)
{
	EnsureNotDisposed();
	if (_registries.TryGetValue(registry.ServiceType, out var existing))
	{
		_registries[registry.ServiceType] = registry;
		registry.Next = existing;
	}
	else
	{
		_registries[registry.ServiceType] = registry;
	}
	return this;
}
这里面是什么意思呢?
因为你可以注册Register<IFoo, Foo>,那么你也可以注册Register<IFoo, Foo1>,也就是说一个IFoo,可以对应多个实例。
当前保存的方式通过链表的方式存储。
这时候其实就注册完了,那么使用的时候如何使用?
GetServices(cat1);是如何创建出Foo的呢?
void GetServices<TService>(Cat cat)
{
      cat.GetService<TService>();
}
具体实现:
public object GetService(Type serviceType)
{
	EnsureNotDisposed();
	if (serviceType == typeof(Cat) || serviceType == typeof(IServiceProvider))
	{
		return this;
	}
	ServiceRegistry registry;
	//IEnumerable<T>
	if (serviceType.IsGenericType && serviceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
	{
		var elementType = serviceType.GetGenericArguments()[0];
		if (!_registries.TryGetValue(elementType, out registry))
		{
			return Array.CreateInstance(elementType, 0);
		}
		var registries = registry.AsEnumerable();
		var services = registries.Select(it => GetServiceCore(it, Type.EmptyTypes)).ToArray();
		Array array = Array.CreateInstance(elementType, services.Length);
		services.CopyTo(array, 0);
		return array;
	}
	//Generic
	if (serviceType.IsGenericType && !_registries.ContainsKey(serviceType))
	{
		var definition = serviceType.GetGenericTypeDefinition();
		return _registries.TryGetValue(definition, out registry)
			? GetServiceCore(registry, serviceType.GetGenericArguments())
			: null;
	}
	//Normal
	return _registries.TryGetValue(serviceType, out registry)
			? GetServiceCore(registry, new Type[0])
			: null;
}
第一种就是:IEnumerable<>这种,获取其泛型参数,然后创建IEnumerable<>
第二种是这种:dictionary<string,string>,如果没有找到的话,那么会去找:dictionary<Tkey,Tvalue>
第三种就属于普通模式了,来看下GetServiceCore。
private object GetServiceCore(ServiceRegistry registry, Type[] genericArguments)
{
	var key = new Key(registry, genericArguments);
	var serviceType = registry.ServiceType;
	switch (registry.Lifetime)
	{
		case Lifetime.Root:return GetOrCreate(_root._services, _root._disposables);
		case Lifetime.Self: return GetOrCreate(_services, _disposables);
		default:
			{
				var service = registry.Factory(this, genericArguments);
				if (service is IDisposable disposable && disposable != this)
				{
					_disposables.Add(disposable);
				}
				return service;
			}
	}
	object GetOrCreate(ConcurrentDictionary<Key, object> services, ConcurrentBag<IDisposable> disposables)
	{
		if (services.TryGetValue(key, out var service))
		{
			return service;
		}
		service = registry.Factory(this, genericArguments);
		services[key] = service;
		if (service is IDisposable disposable)
		{
			disposables.Add(disposable);
		}
		return service;
	}
}
上面源码很好理解:
生成一个key,这个key用来保存当前注册服务和genericArguments。
保存这个的作用在于处理单例模式。
下面有几种模式,一种是_root 模式,因为当前依赖注入可以创建多个实例,但是只有一个是根实例。
var cat1 = root.CreateChild();
public static Cat CreateChild(this Cat cat) => new Cat(cat);
internal Cat(Cat parent)
{
	_root = parent._root;
	_registries = _root._registries;
	_services = new ConcurrentDictionary<Key, object>();
	_disposables = new ConcurrentBag<IDisposable>();
}
第二种就是:Lifetime.Self
获取当前cat的单例模式。
第三种就是每次都创建一个。
重新整理.net core 计1400篇[六] (.net core 一个简易版的依赖注入容器 )的更多相关文章
- [ASP.NET Core 3框架揭秘] 依赖注入[4]:一个Mini版的依赖注入框架
		
在前面的章节中,我们从纯理论的角度对依赖注入进行了深入论述,我们接下来会对.NET Core依赖注入框架进行单独介绍.为了让读者朋友能够更好地理解.NET Core依赖注入框架的设计与实现,我们按照类 ...
 - C#反射与特性(六):设计一个仿ASP.NETCore依赖注入Web
		
目录 1,编写依赖注入框架 1.1 路由索引 1.2 依赖实例化 1.3 实例化类型.依赖注入.调用方法 2,编写控制器和参数类型 2.1 编写类型 2.2 实现控制器 3,实现低配山寨 ASP.NE ...
 - ASP.NET Core 中文文档 第三章 原理(10)依赖注入
		
原文:Dependency Injection 作者:Steve Smith 翻译:刘浩杨 校对:许登洋(Seay).高嵩 ASP.NET Core 的底层设计支持和使用依赖注入.ASP.NET Co ...
 - .NET CORE学习笔记系列(2)——依赖注入[4]: 创建一个简易版的DI框架[上篇]
		
原文https://www.cnblogs.com/artech/p/net-core-di-04.html 本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章从 ...
 - .NET Core的文件系统[5]:扩展文件系统构建一个简易版“云盘”
		
FileProvider构建了一个抽象文件系统,作为它的两个具体实现,PhysicalFileProvider和EmbeddedFileProvider则分别为我们构建了一个物理文件系统和程序集内嵌文 ...
 - Asp.Net Core 进阶(三)—— IServiceCollection依赖注入容器和使用Autofac替换它
		
Asp.Net Core 提供了默认的依赖注入容器 IServiceCollection,它是一个轻量级的依赖注入容器,所以功能不多,只是提供了基础的一些功能,要实现AOP就有点麻烦,因此在实际工作当 ...
 - Asp.net core自定义依赖注入容器,替换自带容器
		
依赖注入 在asp.net core程序中,众所周知,依赖注入基本上贯穿了整个项目,以通用的结构来讲解,控制器层(Controller层)依赖业务层(Service层),业务层依赖于仓储层(Repos ...
 - .NET CORE学习笔记系列(2)——依赖注入[5]: 创建一个简易版的DI框架[下篇]
		
为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在上篇中我们介绍了Cat的基本编程模式,接下来我们就来聊聊Cat的 ...
 - 接上一篇:(四)   控制反转(IOC)/ 依赖注入(DI)
		
spring 核心功能:beans.core.context.expression Spring设计理念 Spring是面向Bean的编程 Spring三个核心组件(Core.Context.Bean ...
 - 如何实现一个简易版的 Spring - 如何实现 AOP(终结篇)
		
前言 在 上篇 实现了 判断一个类的方式是符合配置的 pointcut 表达式.根据一个 Bean 的名称和方法名,获取 Method 对象.实现了 BeforeAdvice.AfterReturni ...
 
随机推荐
- Java UML类图
			
在UML的静态机制中类图是一个重点,它不但是设计人员关心的核心,更是实现人员关注的核心.建模工具也主要根据类图来产生代码.类图在UML的9个图中占据了一个相当重要的地位.James Rumbaugh对 ...
 - Java   //手动输入3个数,并从小到大排序
			
1 //手动输入3个数,并从小到大排序 2 //import java.util.Sanner; 3 4 System.out.println("请输入第一个数:"); 5 Sca ...
 - C++ //vector容器嵌套容器
			
1 //vector容器嵌套容器 2 #include <iostream> 3 #include <string> 4 #include<fstream> 5 # ...
 - [MAUI] 混合开发概念
			
 混合开发的概念是相对与原生开发来说的:App不直接运行原生程序,而是在原生程序中运行一个Web程序,原生程序中包含Web运行时,用于承载Web页面.暂且将原生应用称之为Web容器,Web容器应该能 ...
 - Python | Flask 解决跨域问题
			
Python | Flask 解决跨域问题 系列文章目录 目录 系列文章目录 前言 使用步骤 1. 引入库 2. 配置 1. 使用 CORS函数 配置全局路由 2. 使用 @cross_origin ...
 - 基于R语言的raster包读取遥感影像
			
本文介绍基于R语言中的raster包,读取单张或批量读取多张栅格图像,并对栅格图像数据加以基本处理的方法. 1 包的安装与导入 首先,我们需要配置好对应的R语言包:前面也提到,我们这里选择基于 ...
 - Android WifiDisplay分析二:Wifi display连接过程
			
简介 这一章中我们来看Wifi Display连接过程的建立,包含P2P的部分和RTSP的部分,首先来大致看一下Wifi Display规范相关的东西. HIDC: Human Interface D ...
 - ijkplayer编译-RTSP
			
1.编译平台和版本 使用操作系统 Ubuntu 18.04 使用ndk版本:android-ndk-r14b-linux-x86_64.zip (使用r17c编译会报错) AS版本:4.2.2 1.1 ...
 - STM32 启动代码分析
			
PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明 本文作为本人csdn blog的主站的备份.(Bl ...
 - 使用Servlet实现文件下载
			
一位朋友最近在学习JavaWeb开发,开始学习文件下载操作,他自己尝试着去网上看一些教程,总的来说也不是太了解,就让我和他说说,如何实现文件下载功能.我和他说了一下大致的思路,主要分为前端和后端两部分 ...