本文共 6782 字,大约阅读时间需要 22 分钟。
目录
这是我有关依赖反转原理,IoC容器和依赖注入的文章的第四部分。在本文的上半部分,我试图解释如何构建自己的IoC容器。本文的这一部分使用了上一部分中的大多数代码片段。如果您是该部分的直接访问者,可能会很难理解代码。因此,在开始阅读本文之前,请仔细阅读前面的部分。为了轻松浏览该系列的其他文章,我提供了以下链接:
有时,当我们需要维护对象的状态时,依赖项解析对象的生存期被视为最重要的因素。
我在本文前面的部分中给出的实现是最简单的实现,当有新的请求要解决依赖关系时,它会创建一个新对象。
为了测试这种情况,让我们如下修改代码文件:
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.cs和DIP.Implementation.PrinterWriter.cs中进行更改。
现在更改Consumer实现以测试结果。
class Program{ static void Main(string[] args) { // Create the container object Container container = new Container(); // Register all the dependencies DIRegistration(container); // Prepare the first copy object and do the copy operation Copy copy = new Copy(container); copy.DoCopy(); Console.ReadLine(); // Prepare the second copy object and do the copy operation Copy copy2 = new Copy(container); copy2.DoCopy(); Console.ReadLine(); } static void DIRegistration(Container container) { container.Register(); container.Register (); }}
通过运行该应用程序,您将获得类似的输出,如下所示:
从上面的输出中可以看到,在不同的时间创建了两个Reader对象实例。reader对象的第二个实例不同于reader对象的第一个实例。正如我们预期的那样。但是在某些情况下,您可能需要解析对同一对象的依赖关系,以便维持状态。
本文的这一部分将说明如何为我们的自定义IoC容器实现LifeTimeOptions。Microsoft Unity中具有不同的LifeTimeManager类型,具有各种优点。但是出于理解的目的,我将在我们的容器中实现两个LifeTimeOptions。本文的此部分随附了实现的代码。我们欢迎任何改进实现的建议。
首先从实现开始,让我们创建一个枚举,它将具有与我们将要实现的不同LifeTimeOptions的枚举。
public enum LifeTimeOptions{ 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; }}
随着新类的引入,我们的字典声明iocMap将更改为以下内容:
private Dictionary= new Dictionary ();
接下来,我们需要更新DIP.MyIoCContainer.Container.cs的代码以支持LifeTimeOptions。
public class Container{ private DictionaryiocMap = new Dictionary (); public void Register () { Register (LifeTimeOptions.TransientLifeTimeOptions); } public void Register (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 () { return (T)Resolve(typeof(T)); } public object Resolve(Type typeToResolve) { // Find the registered type for typeToResolve if (!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 those List paramsInfo = ctorInfo.GetParameters().ToList(); List
在上面的代码中,我们修改了现有Register方法,并且还有另一个重载Register方法,它只是更新了解析类型的LifeTimeOption属性。
还修改了Resolve()方法,该方法首先验证LifeTimeOption依赖项。如果将LifeTimeOption指定为ContainerControlledLifeTimeOption,则它将检查依赖项的对象是否早已创建。如果是这样,它将返回创建的对象,否则将创建一个新对象,进行存储,然后将其返回给调用方。
现在,使用LifeTimeOptions.ContainerControlled选项更新依赖项的注册并检查结果。
static void DIRegistration(Container container){ container.Register(LifeTimeOptions.ContainerControlledLifeTimeOption); container.Register (LifeTimeOptions.TransientLifeTimeOption);}
请注意我已经给了IReader依赖LifeTimeOptions.ContainerControlledLifeTimeOption和IWriter依赖LifeTimeOptions.TransientLifeTimeOption。
如下更新Main函数:
static void Main(string[] args){ // Create the container object Container container = new Container(); // Register all the dependencies DIRegistration(container); // Prepare the first copy object and do the copy operation Copy copy = new Copy(container); copy.DoCopy(); Console.ReadLine(); // Prepare the second copy object and do the copy operation Copy copy2 = new Copy(container); copy2.DoCopy(); Console.ReadLine();}
让我们尝试运行代码:
现在您可以看到区别。对于IReader依赖性,我们指定了ContainerControlledLifeTimeOption。因此,容器仅创建一个实例,并且每次调用resolve方法都会返回相同的实例。对于IWriter依赖性,我们指定了TransientLifeTimeOption。因此,容器为每次调用resolve方法创建并返回一个新实例。
Microsoft为Microsoft Unity提供了具有各种功能的依赖关系解析。在中,我将解释Microsoft Unity及其功能。
本文的这一部分说明了有关我们自定义容器的LifeTimeOption实现。撰写本文此部分的目的是获得一些有关管理依赖项解析生存期的知识。Microsoft提供了其他一些功能,我将在本文的下一部分中进行解释。
转载地址:http://mfwhj.baihongyu.com/