接下来介绍一下依赖注入的方式:
- 构造函数注入
- 属性注入
- 方法注入
一、 构造函数注入
我们将介绍单构造函数和多构造函数注入
1) 单构造函数使用自动注入
- 单构造函数自动注入,这里我们使用一个简单的例子具体类MyObject依赖于具体类MyDependentClass。
/// /具体类型的依赖关系 public class MyOjbect { private string description; public string Description { get { return description; } set { description = value; } } public MyOjbect(MyDependentClass myDependent) { this .Description = myDependent.Description; } } /// /注入依赖 MyOjbect objlist = container.Resolve < MyOjbect > (); - 当然除了具体类型我们还可以使用接口或者基类类型作为参数注入依赖,这里我们定义一个接口和一个抽象类。
/// / MyServiceBase是一个抽象类,然后objIMyService继承于该抽象类 /// / IMyService是一个接口,objMyService实现该接口 public class MyOjbect { private string description; public string Description { get { return description; } set { description = value; } } public MyOjbect(MyServiceBase objIMyService, IMyService objMyService) { /// /TODO: }} /// /注册抽象类和接口的映射关系 container.RegisterType < IMyService, MyServiceDependent > (); container.RegisterType < MyServiceBase, DataService > ();MyOjbect objlist = container.Resolve < MyOjbect > ();
2) 多重载构造函数通过属性指定注入构造函数
通过InjectionConstructor属性指定要注入构造函数,和单构造函数功能一样,但要注意一点是当有多重载构造函数,如果我们没有使用属性指定注入构造函数,Unity会根据构造函数参数最多的进行依赖注入,如果不存在唯一最多参数的构造函数(例如:有两个或者两个以上构造函数具有最多参数时候),Unity会抛出异常。
public class MyOjbect { private string description; public string Description { get { return description; } set { description = value; } } public MyOjbect(MyServiceBase objIMyService) { /// / } [InjectionConstructor] public MyOjbect(MyServiceBase objIMyService, IMyService objMyService) { /// / }}
通过前面的介绍我们初步明白了Unity的作用就是给我们一个更方便实现类与类的解耦,假设在一般情况一个类依赖于其他类的时候,我们必须实例化该依赖类,然后把实例传递给我们要调用类,但有了Unity它帮我们实例了这些。OK接下来讲一个具体例子。
/// <summary> /// 父母类 /// </summary> public class Parent { private readonly ChildA _classA; private readonly ChildB _classB; public Parent() { } // 指定依赖注入构造函数 [InjectionConstructor] public Parent(ChildA chA, ChildB chB) { this ._classA = chA; this ._classB = chB; } public override string ToString() { // 年长的父母依赖与孩子。 return string .Format( " The elder depend on {0} and {1}. " , this ._classA.ToString(), this ._classB.ToString()); } } /// <summary> /// 孩子类 /// </summary> public class ChildA : Parent { public override string ToString() { return " ChildA " ; } } /// <summary> /// 孩子类 /// </summary> public class ChildB : Parent { public override string ToString() { return " ChildB " ; } } class Program { static void Main( string [] args) { using (IUnityContainer container = new UnityContainer()) { Parent objParent = container.Resolve < Parent > (); Console.WriteLine(objParent.ToString()); Console.ReadKey(); } } }
接下通过一个简单的配置文件实例例子进一步说明,Unity是如何实现类与类之间的解耦的。
首先我们先定义一个Foo类,它依赖于一个接口ILogger,它有两个实现分别是LoggerA和LoggerB,然后我们的Foo类要调用LoggerA。在一般情况下我们可以实例化一个LoggerA的实例,然后传递给Foo就OK了,这就是hard code给我们带来了一个紧的耦合,如果要我们修改成调用LoggerB,那么我们可以再实例化一个LoggerB对象,眼前看来是可以这样实现,但如果我们工程很大,这种做法是十分危险,当我们使用配置文件可以简单实现,来讲讲简单实现。
static void Main( string [] args) { using (IUnityContainer container = new UnityContainer()) { /// /Parent objParent = container.Resolve <Parent> (); /// /Console.WriteLine(objParent.ToString()); /// /Console.ReadKey(); // 获取指定名称的配置节 UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection( " unity " ); // 获取container名称为CfgClass下的配置 section.Containers[ " CfgClass " ].Configure(container); Foo myFoo = container.Resolve < Foo > (); Console.WriteLine(myFoo.ToString()); Console.ReadKey(); } } App.config中的配置如下:
< configuration > < configSections > <!-- 每次都必须定义的开头 --> < section name ="unity" type ="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" /> </ configSections > <!-- 使用unity的xsd --> <!-- <unity xmlns="http://schemas.microsoft.com/practices/2010/unity"> --> < unity > <!-- 声明一种是用类型 --> < typeAliases > < typeAlias alias ="ILogger" type ="MSEnterpriseUnitity.ILogger, MSEnterpriseUnitity" /> </ typeAliases > < containers > < container name ="CfgClass" > <!-- 设定该类型的映射关系 --> < types > < type type ="ILogger" mapTo ="MSEnterpriseUnitity.LoggerA, MSEnterpriseUnitity" /> </ types > </ container > </ containers > </ unity > </ configuration > 二、方法注入
当一个类的方法依赖于其他类的时候(例如构造函数参数依赖与其他类),在一般情况下通过实例化该被依赖类对象,然后将参数传递给我们依赖类的方法,如果使用方法注入可以避免实例化被依赖类对象,这里就仅仅需要在依赖方法中设置一个属性InjectionMethod就OK了。
2.1方法依赖于具体类
首先我们定义两个类MyTargetOjbect和OtherObject,然后定义在MyTargetOjbect中定义一个方法Initialize(OtherObject dep),该方法的参数依赖于OtherObject类。
/// <summary> /// 依赖类包含一个Description和OutPut方法。 /// </summary> public class OtherObject { private string _description; public string Description { get { return _description; } set { _description = value; } } public void OutPut() { Console.WriteLine( this .Description); } } /// <summary> /// 目标类的方法Initialize参数依赖于OtherObject类。 /// </summary> public class MyTargetOjbect { public OtherObject dependentObject; [InjectionMethod] public void Initialize(OtherObject dep) { this .dependentObject = dep; } } using (IUnityContainer container = new UnityContainer()) { MyTargetOjbect myTargetObject = container.Resolve < MyTargetOjbect > (); myTargetObject.dependentObject.Description = " Injection successful. " ; myTargetObject.dependentObject.OutPut(); } 2.2 方法依赖于接口或者基类
定义一个抽象类MyBaseClass,然后MyInheritBase继承该抽象类,定义一个接口IMyInterface然后ImplementInterface实现该接口。
/// <summary> /// 接口包含一个属性和一个方法 /// </summary> public interface IMyInterface { string Description { get ; set ; } void OutPut(); } /// <summary> /// 实现IMyInterface的属性和方法 /// </summary> public class ImplementInterface : IMyInterface { private string _description; #region IMyInterface 成员 public string Description { get { return _description; } set { _description = value; } } public void OutPut() { Console.WriteLine( this .Description); } #endregion } /// <summary> /// 一个抽象类包含一个属性和一个方法 /// </summary> public abstract class MyBaseClass { private string _description; public virtual string Description { get { return _description; } set { _description = value; } } public abstract void OutPut(); } /// <summary> /// 实现抽象类的抽象方法 /// </summary> public class MyInheritBase : MyBaseClass { public override void OutPut() { Console.WriteLine( this .Description); } } /// <summary> /// 目标类的方法Initialize参数依赖于OtherObject类。 /// </summary> public class MyTargetOjbect { public IMyInterface myInterface; public MyBaseClass myBase; [InjectionMethod] public void Initialize(IMyInterface myInterface, MyBaseClass myBase) { this .myInterface = myInterface; this .myBase = myBase; }} /// /设置抽象类和接口的映射关系 container.RegisterType < MyBaseClass, MyInheritBase > (); container.RegisterType < IMyInterface, ImplementInterface > (); MyTargetOjbect myTargetObject = container.Resolve < MyTargetOjbect > (); myTargetObject.myInterface.Description = " Injection Successful. " ; myTargetObject.myInterface.OutPut(); myTargetObject.myBase.Description = " Injection Successful. " ; myTargetObject.myBase.OutPut(); 三、属性注入
当一个类的属性依赖于其他类,一般情况初始化该属性需要实例化该类型对象,然而我们可以使用Dependency属性,指定属性注入无限手动实例化类型对象。
3.1 具体类型属性
public class MyObject{ private SomeOtherObject _dependentObject; [Dependency] public SomeOtherObject DependentObject { get { return _dependentObject; } set { _dependentObject = value; } }} IUnityContainer uContainer = new UnityContainer();MyObject myInstance = uContainer.Resolve < MyObject > (); // now access the property containing the dependency SomeOtherObject depObj = myInstance.DependentObject; 3.2抽象类或接口属性
public class MyObject{ private IMyInterface _interfaceObj; private MyBaseClass _baseObj; [Dependency] public IMyInterface InterfaceObject { get { return _interfaceObj; } set { _interfaceObj = value; } } [Dependency] public MyBaseClass BaseObject { get { return _baseObj; } set { _baseObj = value; } }} IUnityContainer uContainer = new UnityContainer() .RegisterType < IMyInterface, FirstObject > () .RegisterType < MyBaseClass, SecondObject > ();MyObject myInstance = uContainer.Resolve < MyObject > (); // now access the properties containing the dependencies IMyInterface depObjA = myInstance.InterfaceObject;MyBaseClass depObjB = myInstance.BaseObject; 3.3 给属性注入命名
给属性命名只需在Dependency(“name”)定义一个名字就OK了。
public class MyTargetOjbect { public IMyInterface myInterface; public MyBaseClass myBase; private IMyInterface _MyProperty1, _MyProperty2; // 命名属性注入 [Dependency( " Property2 " )] public IMyInterface MyProperty2 { get { return _MyProperty2; } set { _MyProperty2 = value; } } // 命名属性注入 [Dependency( " Property1 " )] public IMyInterface MyProperty1 { get { return _MyProperty1; } set { _MyProperty1 = value; } } } // 调用Property1属性注入 container.RegisterType < IMyInterface, ImplementInterface > ( " Property1 " ); // 调用Property2属性注入 container.RegisterType < IMyInterface, ImplementInterface2 > ( " Property2 " ); MyTargetOjbect myTargetObject = container.Resolve < MyTargetOjbect > (); IMyInterface myProperty1 = myTargetObject.MyProperty1; IMyInterface myProperty2 = myTargetObject.MyProperty2; 3.4 构造函数参数的属性注入
构造函数参数依赖于其他类时候,我们可以考虑使用构造函数注入或者属性注入
public class Foo { private ILogger _iLogger; public Foo([Dependency] ILogger iLogger) { this ._iLogger = iLogger; } public override string ToString() { return string .Format( " Foo depends on {0}. " , this ._iLogger.ToString()); } }