目录

介绍

背景

LifeTimeOption的实现

总结


  • 下载源代码18.5 KB

介绍

这是我有关依赖反转原理,IoC容器和依赖注入的文章的第四部分。在本文的上半部分,我试图解释如何构建自己的IoC容器。本文的这一部分使用了上一部分中的大多数代码片段。如果您是该部分的直接访问者,可能会很难理解代码。因此,在开始阅读本文之前,请仔细阅读前面的部分。为了轻松浏览该系列的其他文章,我提供了以下链接:

  • 第1部分:依赖倒置原则
  • 第2部分:控制反转和IoC容器
  • 第3部分:自定义IoC容器
  • 第4部分:具有生命周期选项的自定义IoC容器(当前正在阅读)
  • 第5部分:使用Microsoft Unity的依赖项注入(DI)

背景

有时,当我们需要维护对象的状态时,依赖项解析对象的生存期被视为最重要的因素。

我在本文前面的部分中给出的实现是最简单的实现,当有新的请求要解决依赖关系时,它会创建一个新对象。

为了测试这种情况,让我们如下修改代码文件:

  • 每个对象都将维护一个属性,CreatedOn,以便我们可以跟踪对象的创建时间。
  • 在不同的时间解析多个依赖副本,并比较它们的创建时间。

DIP.Abstractions.IReader.cs

public interface IReader
{DateTime GetCreatedOn();string Read();
}

DIP.Implementation.KeyboardReader.cs

public class KeyboardReader:IReader
{private DateTime _createdOn;public KeyboardReader(){_createdOn = DateTime.Now;}public DateTime GetCreatedOn(){return _createdOn;}public string Read(){return "Reading from \"Keyboard\"";}
}

同样,在DIP.Abstractions.IWriter.csDIP.Implementation.PrinterWriter.cs进行更改。

现在更改Consumer实现以测试结果。

class Program
{static void Main(string[] args){// Create the container objectContainer container = new Container();// Register all the dependenciesDIRegistration(container);// Prepare the first copy object and do the copy operationCopy copy = new Copy(container);copy.DoCopy();Console.ReadLine();// Prepare the second copy object and do the copy operationCopy copy2 = new Copy(container);copy2.DoCopy();Console.ReadLine();}static void DIRegistration(Container container){container.Register<IReader,KeyboardReader>();container.Register<IWriter,PrinterWrter>();}
}

通过运行该应用程序,您将获得类似的输出,如下所示:

从上面的输出中可以看到,在不同的时间创建了两个Reader对象实例。reader对象的第二个实例不同于reader对象的第一个实例。正如我们预期的那样。但是在某些情况下,您可能需要解析对同一对象的依赖关系,以便维持状态。

本文的这一部分将说明如何为我们的自定义IoC容器实现LifeTimeOptions。Microsoft Unity中具有不同的LifeTimeManager类型,具有各种优点。但是出于理解的目的,我将在我们的容器中实现两个LifeTimeOptions。本文的此部分随附了实现的代码。我们欢迎任何改进实现的建议。

LifeTimeOption的实现

首先从实现开始,让我们创建一个枚举,它将具有与我们将要实现的不同LifeTimeOptions的枚举。

public enum LifeTimeOptions
{TransientLifeTimeOption,ContainerControlledLifeTimeOption
}
  • TransientLifeTimeOption:此选项告诉容器每个调用都创建一个新实例(当有调用来解决依赖关系时)
  • ContainerControlledLifeTimeOption:此选项告诉容器我们只希望维护一个解析依赖关系的副本,这样,只要有调用来解决依赖关系,每次都会返回相同的对象)

接下来,我们需要创建一个名为ResolvedTypeWithLifeTimeOptions的新类型,该类型将存储有关已解析类型的信息。

public class ResolvedTypeWithLifeTimeOptions
{public Type ResolvedType { get; set; }public LifeTimeOptions LifeTimeOption { get; set; }public object InstanceValue { get; set; }public ResolvedTypeWithLifeTimeOptions(Type resolvedType){ResolvedType = resolvedType;LifeTimeOption = LifeTimeOptions.TransientLifeTimeOptions;InstanceValue = null;}public ResolvedTypeWithLifeTimeOptions(Type resolvedType, LifeTimeOptions lifeTimeOption){ResolvedType = resolvedType;LifeTimeOption = lifeTimeOption;InstanceValue = null;}
}
  • Resolved 类型:将解决依赖关系的类型
  • LifeTimeOption:创建对象的LifeTime选项
  • InstanceValue:如果LifeTimeOption = ContainerControlledLifeTimeOption,此属性将起作用,并且将为解决依赖关系的所有调用提供相同的对象。

随着新类的引入,我们的字典声明iocMap将更改为以下内容:

private Dictionary<Type, ResolvedTypeWithLifeTimeOptions> = new Dictionary<Type,ResolvedTypeWithLifeTimeOptions>();

接下来,我们需要更新DIP.MyIoCContainer.Container.cs的代码以支持LifeTimeOptions。

public class Container
{private Dictionary<Type, ResolvedTypeWithLifeTimeOptions> iocMap = new Dictionary<Type, ResolvedTypeWithLifeTimeOptions>();public void Register<T1, T2>(){Register<T1, T2>(LifeTimeOptions.TransientLifeTimeOptions);}public void Register<T1, T2>(LifeTimeOptions lifeTimeOption){if (iocMap.ContainsKey(typeof(T1))){throw new Exception(string.Format("Type {0} already registered.", typeof(T1).FullName));}ResolvedTypeWithLifeTimeOptions targetType = new ResolvedTypeWithLifeTimeOptions(typeof(T2), lifeTimeOption);iocMap.Add(typeof(T1), targetType);}public T Resolve<T>(){return (T)Resolve(typeof(T));}public object Resolve(Type typeToResolve){// Find the registered type for typeToResolveif (!iocMap.ContainsKey(typeToResolve))throw new Exception(string.Format("Can't resolve {0}. Type is not registered.", typeToResolve.FullName));ResolvedTypeWithLifeTimeOptions resolvedType = iocMap[typeToResolve];// Step-1: If LifeTimeOption is ContainerControlled and there is //already an instance created then return the created instance.if (resolvedType.LifeTimeOption == LifeTimeOptions.ContainerControlledLifeTimeOptions && resolvedType.InstanceValue != null)return resolvedType.InstanceValue;// Try to construct the object// Step-2: find the constructor //(ideally first constructor if multiple constructors present for the type)ConstructorInfo ctorInfo = resolvedType.ResolvedType.GetConstructors().First();// Step-3: find the parameters for the constructor and try to resolve thoseList<ParameterInfo> paramsInfo = ctorInfo.GetParameters().ToList();List<object> resolvedParams = new List<object>();foreach (ParameterInfo param in paramsInfo){Type t = param.ParameterType;object res = Resolve(t);resolvedParams.Add(res);}// Step-4: using reflection invoke constructor to create the objectobject retObject = ctorInfo.Invoke(resolvedParams.ToArray());resolvedType.InstanceValue = retObject;return retObject;}
}

在上面的代码中,我们修改了现有Register方法,并且还有另一个重载Register方法,它只是更新了解析类型的LifeTimeOption属性。

还修改了Resolve()方法,该方法首先验证LifeTimeOption依赖项。如果将LifeTimeOption指定为ContainerControlledLifeTimeOption,则它将检查依赖项的对象是否早已创建。如果是这样,它将返回创建的对象,否则将创建一个新对象,进行存储,然后将其返回给调用方。

现在,使用LifeTimeOptions.ContainerControlled选项更新依赖项的注册并检查结果。

static void DIRegistration(Container container)
{container.Register<IReader, KeyboardReader>(LifeTimeOptions.ContainerControlledLifeTimeOption);container.Register<IWriter, PrinterWriter>(LifeTimeOptions.TransientLifeTimeOption);
}

请注意我已经给了IReader依赖LifeTimeOptions.ContainerControlledLifeTimeOption和IWriter依赖LifeTimeOptions.TransientLifeTimeOption。

如下更新Main函数:

static void Main(string[] args)
{// Create the container objectContainer container = new Container();// Register all the dependenciesDIRegistration(container);// Prepare the first copy object and do the copy operationCopy copy = new Copy(container);copy.DoCopy();Console.ReadLine();// Prepare the second copy object and do the copy operationCopy copy2 = new Copy(container);copy2.DoCopy();Console.ReadLine();
}

让我们尝试运行代码:

现在您可以看到区别。对于IReader依赖性,我们指定了ContainerControlledLifeTimeOption。因此,容器仅创建一个实例,并且每次调用resolve方法都会返回相同的实例。对于IWriter依赖性,我们指定了TransientLifeTimeOption。因此,容器为每次调用resolve方法创建并返回一个新实例。

Microsoft为Microsoft Unity提供了具有各种功能的依赖关系解析。在下一章中,我将解释Microsoft Unity及其功能。

总结

本文的这一部分说明了有关我们自定义容器的LifeTimeOption实现。撰写本文此部分的目的是获得一些有关管理依赖项解析生存期的知识。Microsoft提供了其他一些功能,我将在本文的下一部分中进行解释。

依赖反转原理,IoC容器和依赖注入:第4部分相关推荐

  1. 依赖反转原理,IoC容器和依赖注入:第5部分

    目录 介绍 总览 依赖注册 实例注册 工厂注册 类型注册 样例应用程序 总结 介绍 这是我关于依赖反转原理和依赖注入的文章的第五部分也是最后一部分.在上一部分中,我实现了一个自定义容器,其功能非常有限 ...

  2. 依赖反转原理,IoC容器和依赖注入:第3部分

    目录 介绍 背景 自定义IoC容器的工作方式 自定义IoC容器 高级模块的使用者 编码自定义IoC容器 步骤1:在Visual Studio中创建一个空白解决方案,并创建以下项目 步骤2:将以下代码添 ...

  3. 依赖反转原理,IoC容器和依赖注入:第2部分

    目录 介绍 控制反转(IoC) 什么是控制反转 接口反转 流反转 创建反转 依赖注入 总结 介绍 这是有关依赖反转原理,IoC容器和依赖注入的文章的继续部分.在本文的上半部分,我解释了什么是依赖反转原 ...

  4. 依赖反转原理,IoC容器和依赖注入:第1部分

    目录 介绍 先决条件 依赖倒置原则(DIP) 总结 介绍 在处理WPF应用程序时,我遇到过诸如Unity容器,IoC,依赖注入之类的术语.当时,我很迷茫,想着这一切的需要.但后来,当我逐渐了解到它的好 ...

  5. spring源码分析第三天------spring核心IOC容器和依赖注入原理

    基于XML的依赖注入 1.依赖注入发生的时间 当 Spring IOC 容器完成了 Bean 定义资源的定位.载入和解析注册以后,IOC 容器中已经管理类 Bean 定义的相关数据,但是此时 IOC ...

  6. Spring框架IOC容器,依赖注入,控制反转

    Spring的配制文件中 以上标签的属性id是不是必须的?不是必须的,如果定义id,那么其它的bean就可以通过此name来引用id 创建beans.xml <?xml version=&quo ...

  7. 控制反转( IoC)和依赖注入(DI)

    控制反转( IoC)和依赖注入(DI) tags: 容器 依赖注入 IOC DI 控制反转 引言:如果你看过一些框架的源码或者手册,像是laravel或者tp5之类的,应该会提到容器,依赖注入,控制反 ...

  8. Spring的控制反转(IOC)和依赖注入(DI)具体解释

    Spring的控制反转(IOC)和依赖注入(DI)具体解释 首先介绍下(IOC)控制反转: 所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的.这样控制器就有应 ...

  9. 大学生 从0开始学Spring第2篇:控制反转(IoC)与依赖注入【从0到精通】

    控制反转(IoC)与依赖注 **引出spring** 依赖关系 spring 容器 IOC:控制反转 DI:依赖注入 总结 Spring中有3个核心的概念: 控制反转(Ioc). 依赖注入(DI). ...

最新文章

  1. DbType,OleDbType,SqlDbType区别
  2. Java 解惑:Comparable 和 Comparator 的区别
  3. SQL Server 数据库连接字符串的声明
  4. ES2017异步函数现已正式可用
  5. 在微服务框架Demo.MicroServer中添加SkyWalking+SkyApm-dotnet分布式链路追踪系统
  6. Python3OS文件/方法
  7. 中国芯片人才大军在哪里?
  8. python画cpk图_TensorFlow——Checkpoint为模型添加检查点的实例
  9. elcipse 中利用maven创建web工程
  10. 通过Visual Studio 2012 比较SQL Server 数据库的架构变更
  11. ussd代码大全_如何运行USSD代码
  12. 从北斗到Mate 50:星空中的中国式浪漫
  13. —— GPS测量原理及应用复习-7 ——
  14. pacman使用教程,官方文档中文版
  15. Android实现二维码扫描功能(三)-闪光灯控制
  16. ELF中的.data段和.bss段
  17. [oeasy]python0072_修改字体前景颜色_foreground_color_font
  18. PS画等分圆环的技巧
  19. java返回给前端的json数据中带有转义符号如何处理
  20. Docker 部署ElasticSearch + Kibana

热门文章

  1. linux 取消nologin_Linux中nologin的应用 转
  2. 数字通信同步技术的matlab与fpga实现_数字通信电缆行业规模情况及趋势分析(附报告目录)...
  3. mmu计算机组成原理,计算机组成原理
  4. python采集抖音数据_DouYinSDK 抖音爬虫数据采集福音
  5. mysql核心技术分析_深入理解MySQL核心技术
  6. 从第一范式到第二范式所做的操作是_数据库设计三大范式
  7. 创意三维c4d形式设计节气海报学习案例
  8. UI设计灵感|不同形式的图标设计
  9. 一样是图标设计,UI 小白应该和老司机学的上手技能,临摹!
  10. mysql+select阻塞ddl_MySQL 5.6中如何定位DDL被阻塞的问题