Unity框架的依赖注入Dependency injection(DI)

发表于2018-11-13
评论0 8.6k浏览
如果大家是第一次接触依赖注入这个概念,可能会不了解如何去使用它,其实只要知道依赖注入是一种方式、方法或者说手段,是让被注入者和注入者之间建立关联的手段。依赖注入的目的是松耦合,是交互对象之间的松耦合。了解这些之后,大家在掌握依赖注入就会非常容易了。

一、构造注入 Constructors injection

构造注入适用于对象强依赖的情况,需要在构造函数中实例化别外一个类型,以控制对象的实例化顺序。已经存在的实例是不能使用构造注入,即不能使用构造注入改变实例属性。

以下情况适合使用构造注入
  • 在实例化父对象时自动实例化子对象
  • 想用一个简单的方法表示代码是类的依赖关系
  • 父对象有能在太多的构造函数
  • 父对象的构造函数不能有太多的参数
  • 需要隐藏对象内部字段的值,而用属性或方法会使其显露
  • 要控制父对象依赖的子对象,需要将依赖从代码中移出时

Unity建议当不确定使用哪种注入时,使用构造注入,除非是要在一个已有的对象实例改变属性时才使用其它注入。

DEMO:
namespace ConsoleUnityDemo.ConstractrInjection
{
    public class Parent
    {
        private SubClass subClass;
        public Parent(SubClass subInstance)
        {
            subClass = subInstance;
        }
        public string SubClassName
        {
            get {return subClass.ClassName;}
        }
    }
    public class SubClass
    {
        public string ClassName{get;set;}
    }
    public class SubClassConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(string))
                return true;
            return base.CanConvertFrom(context, sourceType);
        }
        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            return base.ConvertTo(context, culture, value, destinationType);
        }
        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            if (value is string)
            {
                SubClass model = new SubClass();
                model.ClassName = value.ToString();
                return model;
            }
            return base.ConvertFrom(context, culture, value);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            UnityConfigurationSection unitySection = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
            UnityContainer unityContainer = new UnityContainer();
            unitySection.Configure(unityContainer, "ConstractInject"); //获取容器
            ConstractrInjection.Parent parent = unityContainer.Resolve<ConstractrInjection.Parent>("ParentClass");  //取实例
            Console.WriteLine(string.Format(parent.SubClassName));
        }
    }
}

Unity配置
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <alias alias="Parent" type="ConsoleUnityDemo.ConstractrInjection.Parent, ConsoleUnityDemo" />
    <alias alias="SubClass" type="ConsoleUnityDemo.ConstractrInjection.SubClass, ConsoleUnityDemo" />
    <alias alias="SubClassConverter" type="ConsoleUnityDemo.ConstractrInjection.SubClassConverter, ConsoleUnityDemo" />
    <container name="ConstractInject">
      <register type="Parent" name="ParentClass" mapTo ="Parent">
        <constructor>
          <param name="subInstance">
            <value value="This is SubClass" typeConverter="SubClassConverter"/>
          </param>
        </constructor>
      </register>
    </container>
  </unity>
  <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
<appSettings>
</appSettings>
</configuration>

构造注入存在以下三种情况:

1、单个构造函数时自动注入
当对象只有一个构造函数,Unity会自动实例化构造函数参数所依赖的对象,参数必需是个实体类,不能为接口或抽象类。如
public class BoxA
{
   public BoxA(BoxB box)
  {
  }
}
使用Unity注入时会自动实例化BoxB,在上面的Demo中可以不使用<constructor>节点

2、当参数为抽象类或接口时,可以用[Dependency("objectName")]来指定所依赖的对象,Unity在构造MyObject对象时会自动将参数映射到DataServices对象
public class MyObject
{
  public MyObject([Dependency("DataService")] IMyService myDataService)
  { 
    // work with the service here
  }
} 

3、当有多个构造函数,且构造函数的参数个数一样时,使用InjectionConstructor属性指定Unity默认调用的构造函数。Unity对实例化对象时会自动调用标记为InjectionConstructor的构造函数
public class MyObject
{
  public MyObject(SomeOtherClass myObjA)
  { 
    ...
  }
  [InjectionConstructor]
  public MyObject(MyDependentClass myObjB)
  { 
    ...
  }
} 

二、属性注入Sitter injection

属性注入通入对象公开的属性改变对象的值,比如记录日志时常用的方式,ILogger抽象了日志实体,再根据需要实例化不同的日志实体
public class LogManager
{
    ILogger logInstance;
    [Dependency]
    public ILoger
    {
        get{return logInstance;}
        set{logInstance= value;}
    }
    public void WriteLog()
    {
        logInstance.WriteLog();
    }
}
用Dependency属性向Unity暴露这是一个依赖注入的属性。

属性注入注意时应该始终在要依赖注入的属性上加上Dependency 标签,当然不加也是可以使用的。

三、方法注入Method Call Injection

属性注入时必需要将对象内部的字段对外公开,但某些时候为了封装原则,不想对外公开这些属性时,可以用方法注入。如用Initialize方法代码Set属性
public class LogManager
{
    ILogger logInstance;
  [InjectionMethod]
    public void Initialize(ILogger instance)
    {
        logInstance = instance
    }
    public void WriteLog()
    {
        logInstance.WriteLog();
    }
}

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

标签: