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

1.引言

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

2.構造函數注入

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

先準備幾個類以下:app

    /// <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什麼東東。ide

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();
        }

配置文件方式 以下:post

<?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>

 如下是加載配置文件this

        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特性來指定一個構造函數來解析對象,以下聲明:spa

    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名稱以下:code

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

下面註冊一個名稱爲ec的映射,若是沒有名稱ec的映射將報錯xml

        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的使用了。

 

            IUnityContainer container = new UnityContainer();
            //獲取指定名稱的配置節
            UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");

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

            SplitClass splitClass = container.Resolve<SplitClass>();
            splitClass.ShowInfo();
    //分班
    public class SplitClass
    {
        IClass _class;
        IStudent _student;
        
        public SplitClass()
        { }

        [InjectionConstructor]
        public SplitClass([Dependency("ec")]IClass _class, IStudent student)
        {
            this._class = _class;
            _student = student;
            _student.ToClass = this._class;
        }

        public void ShowInfo()
        {
            Console.WriteLine("{0}學生被安排到班級{1}", _student.Name, _class.ClassName);
        }
    }
相關文章
相關標籤/搜索