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

目录

介绍

背景

LifeTimeOption的实现

总结


介绍

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

背景

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

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

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

  • 每个对象都将维护一个属性,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 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<IReader,KeyboardReader>();
        container.Register<IWriter,PrinterWrter>();
    }
}

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

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

本文的这一部分将说明如何为我们的自定义IoC容器实现LifeTimeOptionsMicrosoft 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 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<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 object
        object 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.ContainerControlledLifeTimeOptionIWriter依赖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方法创建并返回一个新实例。

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

总结

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章