前面俩种实现中,很多内部细节都无法知道,微软的框架也是为了屏蔽具体实现,只让我们关注接口。但是人都是充满好奇的,依赖注入到底是怎么实现的呢?

微软又有怎样的实现呢?下面就为大家一一呈现(说实话,代码真不好读)

先看下核心类:ServiceTable

 internal class ServiceTable
{
private readonly object _sync = new object(); private readonly Dictionary<Type, ServiceEntry> _services;
private readonly Dictionary<Type, List<IGenericService>> _genericServices;
private readonly ConcurrentDictionary<Type, Func<ServiceProvider, object>> _realizedServices = new ConcurrentDictionary<Type, Func<ServiceProvider, object>>(); public ServiceTable(IEnumerable<ServiceDescriptor> descriptors)
{
_services = new Dictionary<Type, ServiceEntry>();
_genericServices = new Dictionary<Type, List<IGenericService>>(); foreach (var descriptor in descriptors)
{
var serviceTypeInfo = descriptor.ServiceType.GetTypeInfo();
if (serviceTypeInfo.IsGenericTypeDefinition)
{
Add(descriptor.ServiceType, new GenericService(descriptor));
}
else if (descriptor.ImplementationInstance != null)
{
Add(descriptor.ServiceType, new InstanceService(descriptor));
}
else if (descriptor.ImplementationFactory != null)
{
Add(descriptor.ServiceType, new FactoryService(descriptor));
}
else
{
Add(descriptor.ServiceType, new Service(descriptor));
}
}
} public ConcurrentDictionary<Type, Func<ServiceProvider, object>> RealizedServices
{
get { return _realizedServices; }
} public bool TryGetEntry(Type serviceType, out ServiceEntry entry)
{
lock (_sync)
{
if (_services.TryGetValue(serviceType, out entry))
{
return true;
}
else if (serviceType.GetTypeInfo().IsGenericType)
{
var openServiceType = serviceType.GetGenericTypeDefinition(); List<IGenericService> genericEntry;
if (_genericServices.TryGetValue(openServiceType, out genericEntry))
{
foreach (var genericService in genericEntry)
{
var closedService = genericService.GetService(serviceType);
if (closedService != null)
{
Add(serviceType, closedService);
}
} return _services.TryGetValue(serviceType, out entry);
}
}
}
return false;
} public void Add(Type serviceType, IService service)
{
lock (_sync)
{
ServiceEntry entry;
if (_services.TryGetValue(serviceType, out entry))
{
entry.Add(service);
}
else
{
_services[serviceType] = new ServiceEntry(service);
}
}
} public void Add(Type serviceType, IGenericService genericService)
{
lock (_sync)
{
List<IGenericService> genericEntry;
if (!_genericServices.TryGetValue(serviceType, out genericEntry))
{
genericEntry = new List<IGenericService>();
_genericServices[serviceType] = genericEntry;
} genericEntry.Add(genericService);
}
}
}

首先看代码的属性:

private readonly Dictionary<Type, ServiceEntry> _services;
private readonly Dictionary<Type, List<IGenericService>> _genericServices;
private readonly ConcurrentDictionary<Type, Func<ServiceProvider, object>> _realizedServices = new ConcurrentDictionary<Type, Func<ServiceProvider, object>>();

ServiceEntry类等后面介绍,可以把它当作能够产生一个object对象的类。所以“_services”是存放类型和实例对应关系的字典。

“_genericServices”顾名思义,肯定和泛型有关系,实际上“_genericServices”的Type是类似于“List<T>”这种包含泛型定义的类型。

最后一个_realizedServices定义比较复杂。可能看不明白是什么意思。实际也是定义了一个字典表(ConcurrentDictionary),与Dictionary不同的是,对多线程支持较好。字典表的第一个泛型实参是Type,第二参数是一个Func代理;而这个Func代理(可以百度C#+Func查询详细的用法)的入参是ServiceProvider,返回值是object.

[在注入获取实例时,会查询_realizedServices是否已经包含该实例,如果有则查询,如果没有则到_services中生产一个,之后将生成的结果加到realizedServices中,并且返回;但是如果_services也没有呢,则将_genericServices中泛型进行类型“实参话”,将所有类型匹配的都加入到_services中,之后从_services中获取]

从类的构造函数中可以发现,根据ServiceDescriptor对象构建IService(IGenericService)顺序是:GenericService->InstanceService->FactoryService->Service(和Autofac、Ninject是不同的)。

接口定义:IGenericService、IService、IServiceCallSite

internal interface IGenericService
{
ServiceLifetime Lifetime { get; }
IService GetService(Type closedServiceType);
}
internal interface IService
{
IService Next { get; set; }
ServiceLifetime Lifetime { get; }
IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain);
}
internal interface IServiceCallSite
{
object Invoke(ServiceProvider provider);
Expression Build(Expression provider);
}

IServiceCallSite接口:类似一个工厂类,能够通过Invoke调用生成object对象,也就是依赖注入最后产生的对象都是由该接口的实例负责。

IService接口:Lifetime生命周期,CreateCallSite通过ServiceProvider创建IServiceCallSite,也就是能够间接创建实例对象。我觉得Next属性放到接口里不算太恰当,Next对象引用自己,使得IService具有链性结构。

IGenericService接口:产生一个IService接口。

[此处有一个可以思考的问题,IGenericService和IService设计成俩个接口是容易理解的,泛型和非泛型差距很大,可以不共用一个接口。但是IService接口和IServiceCallSite接口为什么不可以合并成一个接口?接口定义如下所示:

 internal interface IService
{
ServiceLifetime Lifetime { get; }
object Invoke(ServiceProvider provider);
}

]

ServiceEntry类

internal class ServiceEntry
{
private object _sync = new object(); public ServiceEntry(IService service)
{
First = service;
Last = service;
} public IService First { get; private set; }
public IService Last { get; private set; } public void Add(IService service)
{
lock (_sync)
{
Last.Next = service;
Last = service;
}
}
}

ServiceEntry

ServiceEntry类相对比较简单。相当于一个IService的单向链表,之后包含了链表的起始节点,以及结束节点。Add方法能够让新节点添加到链表的尾部。
[百思不得其解,为啥不使用LinkList这种列表结构,或者自己写个泛型类的列表,在IService内添加自引用,总觉得不是一种良好的设计]

回头我们看下ServiceTableDictionary<Type, ServiceEntry> _services属性,实际上是对某个类型,注册了很多个IService接口,并且这些接口是按照列表顺序存放的。

回头看看ServiceTable

俩个Add方法很简单,判断该Type类型的列表/数组是否存在,如果存在,调用列表/数组的添加方法,不存在则创建一个。

比较有意思的是TryGet方法,先在非泛型下搜索,如果不存在,则到泛型下搜索。并且将泛型“实参化”。这段代码值得我们深入研究。

 public bool TryGetEntry(Type serviceType, out ServiceEntry entry)
{
lock (_sync)
{
if (_services.TryGetValue(serviceType, out entry))
{
return true;
}
else if (serviceType.GetTypeInfo().IsGenericType)
{
var openServiceType = serviceType.GetGenericTypeDefinition(); List<IGenericService> genericEntry;
if (_genericServices.TryGetValue(openServiceType, out genericEntry))
{
foreach (var genericService in genericEntry)
{
var closedService = genericService.GetService(serviceType);
if (closedService != null)
{
Add(serviceType, closedService);
}
} return _services.TryGetValue(serviceType, out entry);
}
}
}
return false;
}

TryGetEntry

[ServiceTable的俩个属性Dictionary<Type, ServiceEntry> _services和Dictionary<Type, List<IGenericService>> _genericServices这俩个属性中一个使用单向列表,一个使用list,有洁癖的朋友会感觉很不爽有没有,有没有。为啥不能统一了让我们舒舒服服的呢]

[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(1)的更多相关文章

  1. [Asp.net 5] DependencyInjection项目代码分析-目录

    微软DI文章系列如下所示: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Autofac [ ...

  2. [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(5)(IEnumerable<>补充)

    Asp.net 5的依赖注入注入系列可以参考链接: [Asp.net 5] DependencyInjection项目代码分析-目录 我们在之前讲微软的实现时,对于OpenIEnumerableSer ...

  3. [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(3)

    这个系列已经写了5篇,链接地址如下: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Auto ...

  4. [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(4)

    这个系列已经写了6篇,链接地址如下: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Auto ...

  5. [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(2)

    在 DependencyInjection项目代码分析4-微软的实现(1)中介绍了“ServiceTable”.“ServiceEntry”.“IGenericService”.“IService”. ...

  6. [Asp.net 5] DependencyInjection项目代码分析

    最近在研究开源代码,正好发现Asp.net5的源码,下载地址:https://github.com/aspnet. 今天主要讲的是DependencyInjection这部分,抛砖引玉,供大家参考,也 ...

  7. [Asp.net 5] DependencyInjection项目代码分析2-Autofac

    Microsoft.Framework.DependencyInjection.Autofac源码分析 该工程只有一个代码静态类AutofacRegistration,但是该类有3个扩展方法,以及3个 ...

  8. [Asp.net 5] DependencyInjection项目代码分析3-Ninject

    Microsoft.Framework.DependencyInjection.Ninject 该工程内部共包含5个类文件,底层使用Ninject实现依赖注入,工程截图如下: 从文件命名可以看出,Ni ...

  9. Jenkins+Gradle+Sonar进行Java项目代码分析

    Jenkins+Maven+Sonar与Jenkins+Gradle+Sonar配置方法很相似,区别就是Java项目所用的编译工具不同,一个是maven,一个是gradle 使用maven编译工具的可 ...

随机推荐

  1. 【C语言学习】《C Primer Plus》第10章 数组和指针

    学习总结 1.数组初始化方式: int a[]={1,2,3} int a[SIZE]={1,2,3} //SIZE是宏定义,数组初始化个数不能大于SIZE,否则报错:当个数小 //SIZE,自动补0 ...

  2. 今天心情好,给各位免费呈上200兆SVN代码服务器一枚,不谢!

    开篇先给大家讲个我自己的故事,几个月前在网上接了个小软件开发的私活,平日上班时间也比较忙,就中午一会儿休息时间能抽出来倒腾着去做点.每天下班复制一份到U盘带回去继续摸索,没多久U盘里躺着的文件列表那叫 ...

  3. 谈谈对BPM的理解

    BPM的产生缘由 近年来,随着计算机技术的发展和互联网时代的到来,我们已经进入了信息时代,也称为数字化时代,在这数字化的时代里,企业的经营管理都受到了极大的挑战.从上世纪90年代起至今,企业的信息化工 ...

  4. [nRF51822] 4、 图解nRF51 SDK中的Schedule handling library 和Timer library

    :nRF51822虽然是一个小型的单片机,但是能真正达到任意调用其官方驱动以及BLE协议栈的人还是奇缺的.据我所见,大都拿官方给的一个冗长的蓝牙低功耗心率计工程改的.之前我对于这个工程进行log跟踪, ...

  5. 用DirectX实现魔方(三)视角变换及缩放(附源码)

    在本系列第一篇介绍过鼠标按键的功能,如下. 左键拖拽 - 旋转魔方 右键拖拽 - 变换视角 滚轮 - 缩放魔方 今天研究一下如何实现后面两个功能,用到的技术主要是Arcball,Arcball是实现M ...

  6. XML学习笔记7——XSD实例

    在前面的XSD笔记中,基本上是以数据类型为主线来写的,而在我的实际开发过程中,是先设计好了XML的结构(元素.属性),并写好了一份示例,然后再反过来写XSD文件(在工具生成的基础上修改),也就是说,是 ...

  7. 使用Connetion的属性RetainSameConnection

    在SSIS的组件中,很多都会连接到数据库进行操作,Connection有一个属性RetainSameConnection,默认值是False,控制着连接的打开和关闭的时机. 1,如果Connectio ...

  8. fabric upgrade from old crashlystic stuck in build

    提示build,一直没有反应. 问题:工程中原有的shell命令屏蔽掉了

  9. Sort the Array

    /* 思路: 找到单调下降串的起始位置[l, r] 如果左边 0...l-1中的最大值 > l...r中的最小值 或者 r+1...n中的最小值 < l...r中的最大值 都是不能实现排序 ...

  10. 面试准备 - HashTable 的C#实现 开放地址法

    Hashtable是很经常在面试中遇到的数据结构,因为他的O(1)操作时间和O(n)空间 之所以自己写一份是因为: 加深对于hashtable的理解 某些公司面试的时候需要coding....... ...