[IoC容器Unity]第三回:依賴注入


1.引言

上節介紹了,Unity的Lifetime Managers生命周期,Unity具體實現依賴注入包含構造函數注入、屬性注入、方法注入,所謂注入相當賦值,下面一個一個來介紹。

2.構造函數注入

Unity利用Resolve方法解析一個對象,都是調用注冊類型的構造函數來初始化的,初始化時,Unity能夠控制初始化的值,當然,我們要給Unity提供足夠的原料,要不然也是巧婦難無米之炊,下面看一些簡單的示例。

先准備幾個類如下:

復制代碼
    /// <summary>
/// 班級接口
/// </summary>
public interface IClass
{
string ClassName { get; set; }

void ShowInfo();
}
/// <summary>
/// 計科班
/// </summary>
public class CbClass : IClass
{
public string ClassName { get; set; }

public void ShowInfo()
{
Console.WriteLine(
"計科班:{0}", ClassName);
}
}
/// <summary>
/// 電商班
/// </summary>
public class EcClass : IClass
{
public string ClassName { get; set; }

public void ShowInfo()
{
Console.WriteLine(
"電商班:{0}", ClassName);
}
}

/// <summary>
/// 學生接口
/// </summary>
public interface IStudent
{
string Name { get; set; }
//就讀班級
void ShowInfo();
}
/// <summary>
/// 學生
/// </summary>
public class QlinStudent : IStudent
{
public string Name { get; set; }

private IClass ToClass { get; set; }

public QlinStudent(IClass _class)
{
ToClass
= _class;
}

public void ShowInfo()
{
Console.WriteLine(
"{0}就讀班級:{1}", Name, ToClass.ClassName);
}
}
復制代碼

是一個班級和學生的結構,現在我們要解析一個學生IStudent,我們看到具體學生類QlinStudent的構造函數需要一個班級接口,當然要 給IUnityContainer容器提供這個班級映射還有學生自己的映射,就你要什么東東,首先要提供IUnityContainer什么東東。

2.1 默認方式

默認方式跟new一個對象,它會根據你提供的材料,選擇一個構造函數,即要有構造器要能訪問權限,用Public修飾,構造函數的參數也要提供,即IClass也要能解析,不然就報錯了,編程注入方式如下:

復制代碼
        public static void ConStructorCodeTest1()
{
IUnityContainer container
= new UnityContainer();
//默認注冊(無命名),如果后面還有默認注冊會覆蓋前面的
container.RegisterType<IClass, CbClass>();
container.RegisterType
<IStudent, QlinStudent>();
//解析默認對象
IStudent splitClass = container.Resolve<IStudent>();
splitClass.ShowInfo();
}
復制代碼

配置文件方式 如下:

復制代碼
<?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">
<!--引用命名空間-->
<namespace name="ConsoleApplication1.UnityDemo.Constructor" />
<!--引用程序集-->
<assembly name="ConsoleApplication1" />
<!--容器-->
<container name="FirstClass">
<!--映射關系-->
<register type="IClass" mapTo="CbClass"></register>
<register type="IClass" name="ec" mapTo="EcClass"></register>
<register type="IStudent" mapTo="QlinStudent">

</register>
</container>
</unity>
</configuration>
復制代碼

 以下是加載配置文件

復制代碼
        public static void ConStructorConfigTest1()
{
IUnityContainer container
= new UnityContainer();
string configFile = "http://www.cnblogs.com/UnityDemo/Constructor/Unity.config";
var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = configFile };
//從config文件中讀取配置信息
Configuration configuration =
ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
//獲取指定名稱的配置節
UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection("unity");

//載入名稱為FirstClass 的container節點
container.LoadConfiguration(section, "FirstClass");

IStudent splitClass
= container.Resolve<IStudent>();
splitClass.ShowInfo();
}
復制代碼

 

2.2 指定構造函數

如果構造函數有多個,它也會按照上面那樣來初始化一個對象,我們還可以顯示用InjectionConstructor特性來指定一個構造函數來解析對象,如下聲明:

復制代碼
    public class QlinStudent : IStudent
{
private string Name { get; set; }

private IClass ToClass { get; set; }


public QlinStudent()
{
}

[InjectionConstructor]
public QlinStudent(IClass _class,string name)
{
ToClass
= _class;
Name
= name;
}

public void ShowInfo()
{
Console.WriteLine(
"{0}就讀班級:{1}", Name, ToClass.ClassName);
}
}
復制代碼

 

2.3 指定參數依賴的注冊名稱

構造函數中IClass參數,如果IUnityContainer注冊了多個,默認是使用無名稱的那個注冊,也可以通過Dependency依賴哪個名稱來指定哪個來注冊,代碼,指定ec名稱如下:

        [InjectionConstructor]
public QlinStudent([Dependency("ec")]IClass _class)
{
ToClass
= _class;
}

下面注冊一個名稱為ec的映射,如果沒有名稱ec的映射將報錯

復制代碼
        public static void ConStructorCodeTest1()
{
IUnityContainer container
= new UnityContainer();

//默認注冊(無命名),如果后面還有默認注冊會覆蓋前面的
container.RegisterType<IClass, CbClass>();
//命名注冊
container.RegisterType<IClass, EcClass>("ec");
container.RegisterType
<IStudent, QlinStudent>();

//解析默認對象
IStudent splitClass = container.Resolve<IStudent>();
splitClass.ShowInfo();
}
復制代碼

配置文件方式,代碼不變,配置中添加一個 name屬性就行,如下:

復制代碼
    <container name="FirstClass">
<!--映射關系-->
<register type="IClass" mapTo="CbClass"></register>
<register type="IClass" name="ec" mapTo="EcClass"></register>
<register type="IStudent" mapTo="QlinStudent">
</register>
</container>
復制代碼

 

2.4 指定參數值

構造器中的參數也可以依賴一個指定的類型值,如下代碼依賴於EcClass類型,可以讓構造函數中可以傳入一個具體的類型,這也是構造函數傳參數,如下:

復制代碼
        public static void ConStructorCodeTest1()
{
IUnityContainer container
= new UnityContainer();

//默認注冊(無命名),如果后面還有默認注冊會覆蓋前面的
container.RegisterType<IClass, CbClass>();
//命名注冊
container.RegisterType<IClass, EcClass>("ec");
container.RegisterType
<IStudent, QlinStudent>(new InjectionConstructor(new CbClass()));
IStudent splitClass
= container.Resolve<IStudent>();
splitClass.ShowInfo();
}
復制代碼

或者注冊一個實例對象,如下:

復制代碼
        public static void ConStructorCodeTest1()
{
IUnityContainer container
= new UnityContainer();
IClass cbClass
= new CbClass { ClassName="計科051班" };
//實例注冊命名實例
container.RegisterInstance<IClass>("ec", cbClass);
container.RegisterType
<IStudent, QlinStudent>();
IStudent splitClass
= container.Resolve<IStudent>();
splitClass.ShowInfo();
}
復制代碼

 

配置文件也可以指定類型依賴,如下,指定EcClass:

復制代碼
      <register type="IStudent"  mapTo="QlinStudent">
<constructor>
<param name="_class" type="IClass">
<dependency type="EcClass"/>
</param>
</constructor>
</register>
復制代碼

上面已經介紹了傳參數,是用InjectionConstructor類型,現在構造函數,多一個參數,如下:

        [InjectionConstructor]
public QlinStudent([Dependency("ec")]IClass _class, string name)
{
ToClass
= _class;
Name
= name;
}

多了一個name參數,那必須為容器IUnityContainer提供這個參數,沒有這個原材料,它無法構造,就會報錯,如下代碼:

復制代碼
        public static void ConStructorCodeTest1()
{
IUnityContainer container
= new UnityContainer();

container.RegisterType
<IStudent, QlinStudent>(new InjectionConstructor(new CbClass() { ClassName = "計科051" }, "Qlin"));
IStudent splitClass
= container.Resolve<IStudent>();
splitClass.ShowInfo();
}
復制代碼

注入參數后,也可以下次解析的時候,通過ParameterOverrides類來覆蓋原來的參數,改變參數值,如下:

復制代碼
        public static void ConStructorCodeTest1()
{
IUnityContainer container
= new UnityContainer();
container.RegisterType
<IStudent, QlinStudent>(new InjectionConstructor(new CbClass() { ClassName = "計科051" }, "Qlin"));
IStudent student
= container.Resolve<IStudent>();
student.ShowInfo();

//覆蓋參數解析
IStudent student1 = container.Resolve<IStudent>(new ParameterOverrides()
{
{
"_class",new EcClass(){ ClassName="電商051"}},
{
"name","linq"}
});
student1.ShowInfo();
}
復制代碼

 

3.屬性注入

就是Unity容器解析對象時,為屬性賦值,有操作權限要Public修飾屬性。屬性注入方式和構造函數注入類似,只需在需要注入的屬性上增加一個 Dependency特性,Dependency指定一個注冊名稱name參數用來指定注入對象的名稱,屬性注入也是伴隨着類型初始化時注入的,在解析時 自動注入,所以解析時跟以前一樣。代碼修改如下,在ToClass屬性上增加了Dependency特性,來表示這個屬性需要注入:

復制代碼
    public class QlinStudent : IStudent
{
public string Name { get; set; }

[Dependency(
"ec")]
public IClass ToClass { get; set; }

public void ShowInfo()
{
Console.WriteLine(
"{0}就讀班級:{1}", Name, ToClass.ClassName);
}
}
復制代碼

代碼方式如下:

            IUnityContainer container = new UnityContainer();
container.RegisterType
<IClass, EcClass>("ec");
container.RegisterType
<IStudent, QlinStudent>();
IStudent splitClass
= container.Resolve<IStudent>();
splitClass.ShowInfo();

配置文件方式,依賴的<dependency name="ec1" name值 可指定注冊時注冊的名稱:

復制代碼
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<!--引用命名空間-->
<namespace name="ConsoleApplication1.UnityDemo.Constructor4" />
<!--引用程序集-->
<assembly name="ConsoleApplication1" />
<!--容器-->
<container name="FirstClass">
<!--映射關系-->
<register type="IClass" mapTo="CbClass">
</register>
<register type="IClass" name="ec1" mapTo="EcClass">
<property name="ClassName" propertyType="System.String" value="電商051" />
</register>
<register type="IStudent" mapTo="QlinStudent">
<property name="ToClass">
<dependency name="ec1" type="EcClass"/>
</property>
</register>
</container>
</unity>
復制代碼

 

調用效果圖:

4.方法注入

用public修飾方法,方法注入也是跟構造函數類似代碼修改如下

復制代碼
    public class QlinStudent : IStudent
{
public string Name { get; set; }

private IClass ToClass { get; set; }

[InjectionMethod]
public void InitClass(IClass _class)
{
ToClass
= _class;
}

public void ShowInfo()
{
Console.WriteLine(
"{0}就讀班級:{1}", Name, ToClass.ClassName);
}
}
復制代碼

編程方式注入不變,就是初始化時,注入值,如下:

            IUnityContainer container = new UnityContainer();
container.RegisterType
<IClass, EcClass>();
container.RegisterType
<IStudent, QlinStudent>();
IStudent student
= container.Resolve<IStudent>();
student.ShowInfo();

配置文件方式:

復制代碼
  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<!--引用命名空間-->
<namespace name="ConsoleApplication1.UnityDemo.Constructor5" />
<!--引用程序集-->
<assembly name="ConsoleApplication1" />
<!--容器-->
<container name="FirstClass">
<!--映射關系-->
<register type="IClass" mapTo="CbClass">
</register>
<register type="IClass" name="ec1" mapTo="EcClass">
<property name="ClassName" propertyType="System.String" value="電商051" />
</register>
<register type="IStudent" mapTo="QlinStudent">
<property name="Name" propertyType="System.String" value="Qlin" />
<method name="InitClass">
<param name="_class" type="IClass">
<dependency name="ec1" type="EcClass"/>
</param>
</method>
</register>
</container>
</unity>
復制代碼

 

5.小結

 介紹了3種依賴注入方式,平時主要也就用到這么幾種,其它還有復雜的像擴展容器等,通過本節,基本知道Unity的使用了。

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com