[IoC容器Unity]第二回:Lifetime Managers生命周期


1.引言

Unity的生命周期是注冊的類型對象的生命周期,而Unity默認情況下會自動幫我們維護好這些對象的生命周期,我們也可以顯示配置對象的生命周期,Unity將按照配置自動管理,非常方便,下面就介紹一下 Unity中內置的生命周期管理器。

2.Lifetime Managers生命周期管理

准備以下類關系,用於演示

有2個接口類:IClass(班級接口)和ISubject(科目接口),其分別有2個實現類。看下面一個示例

public static void RegisterInstance()
{
    IClass myClass = new MyClass();
    IClass yourClass = new YourClass();
    //為myClass實例注冊默認實例
    container.RegisterInstance<IClass>(myClass);
    //為yourClass實例注冊命名實例,同RegisterType
    container.RegisterInstance<IClass>("yourInstance", yourClass);
 
    container.Resolve<IClass>().ShowInfo(); 
    container.Resolve<IClass>("yourInstance").ShowInfo();
}

這段代碼很簡單,就是通過RegisterInstance方法為已存在的對象進行注冊,這樣可以通過UnityContainer來管理這些對象實例的生命周期。

需要注意的是,使用RegisterInstance來將已存在的 實例注冊到UnityContainer中,默認情況下其實用的是ContainerControlledLifetimeManager,這個生命周期 是由UnityContainer來進行管理,UnityContainer會維護一個對象實例的強引用,當你將已存在的實例注冊到 UnityContainer后,每次通過Resolve方法獲取對象都是同一對象,也就是單件實例(singleton instance),具體有關生命周期相關信息在下面進行介紹。

由於RegisterInstance是對已存在的實例進行注冊,所以無法通過配置文件來進行配置。有關RegisterInstance方法的其他重載我這邊就不詳細介紹了,可以點此查看詳細的重載方法介紹。

下面將詳細介紹各個Unity的生命周期管理

2.1 TransientLifetimeManager

瞬態生命周期,默認情況下,在使用RegisterType進行對象關系注冊時如果沒有指定生命周期管理器則默認使用這個生命周期管理器,這個生命周期管理器就如同其名字一樣,當使用這種管理器的時候,每次通過ResolveResolveAll調用對象的時候都會重新創建一個新的對象。

需要注意的是,使用RegisterInstance對已存在的對象進行關系注冊的時候無法指定這個生命周期,否則會報異常。

代碼如下:

public static void TransientLifetimeManagerCode()
{
    //以下2種注冊效果是一樣的
    container.RegisterType<IClass, MyClass>();
    container.RegisterType<IClass, MyClass>(new TransientLifetimeManager());
    Console.WriteLine("-------TransientLifetimeManager Begin------");
    Console.WriteLine("第一次調用RegisterType注冊的對象HashCode:" +
        container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第二次調用RegisterType注冊的對象HashCode:" +
        container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("-------TransientLifetimeManager End------");
}

配置文件如下:

<register type="IClass" mapTo="MyClass">
  <lifetime type="transient" />
  <!--<lifetime type="SessionLifetimeManager"
      value="Session#1" typeConverter="SessionLifetimeConverter" />-->
</register>

如果想在配置文件中在在注冊關系的時候更改一個生命周期管理器只需在<register>配置節下新增<lifetime>既可(如果不新增則默認使用TransientLifetimeManager)。

其中<lifetime>有3個參數:

1)type,生命期周期管理器的類型,這邊可以選擇Unity內置的,也可以使用自定義的,其中內置的生命周期管理器會有智能提示。

2)typeConverter,生命周期管理器轉換類,用戶自定義一個生命周期管理器的時候所創建一個轉換器。

3)value,初始化生命周期管理器的值。

配置文件讀取代碼:

public static void TransientLifetimeManagerConfiguration()
{
    //獲取指定名稱的配置節
    UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
    container.LoadConfiguration(section, "First");
 
    Console.WriteLine("-------TransientLifetimeManager Begin------");
    Console.WriteLine("第一次調用RegisterType注冊的對象HashCode:" +
        container.Resolve<IClass>("transient").GetHashCode());
    Console.WriteLine("第二次調用RegisterType注冊的對象HashCode:" +
        container.Resolve<IClass>("transient").GetHashCode());
    Console.WriteLine("-------TransientLifetimeManager End------");
}

效果圖如下,可以看出每次產生的對象都是不同的:

pic72

 

2.2 ContainerControlledLifetimeManager

容器控制生命周期管理,這個生命周期管理器是RegisterInstance默認使用的生命周期管理器,也就是單件實例,UnityContainer會維護一個對象實例的強引用,每次調用的時候都會返回同一對象,示例代碼如下:

 

public static void ContainerControlledLifetimeManagerCode()
{
    IClass myClass = new MyClass();
    //以下2種注冊效果是一樣的
    container.RegisterInstance<IClass>("ccl", myClass);
    container.RegisterInstance<IClass>("ccl", myClass, new ContainerControlledLifetimeManager());
 
    container.RegisterType<IClass, MyClass>(new ContainerControlledLifetimeManager());
    Console.WriteLine("-------ContainerControlledLifetimeManager Begin------");
    Console.WriteLine("第一次調用RegisterType注冊的對象HashCode:" +
        container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第二次調用RegisterType注冊的對象HashCode:" +
        container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第一次調用RegisterInstance注冊的對象HashCode:" +
        container.Resolve<IClass>("ccl").GetHashCode());
    Console.WriteLine("第二次調用RegisterInstance注冊的對象HashCode:" +
        container.Resolve<IClass>("ccl").GetHashCode());
    Console.WriteLine("-------ContainerControlledLifetimeManager End------");
}

配置文件如下:

<register type="IClass" mapTo="MyClass" name="ccl">
  <lifetime type="singleton" />
</register>

效果圖如下,可以看出每次獲取的對象都是同一對象:

pic73

2.3 HierarchicalLifetimeManager

分層生命周期管理器,這個管理器類似於ContainerControlledLifetimeManager,也是由UnityContainer來管理,也就是單件實例。不過與ContainerControlledLifetimeManager不 同的是,這個生命周期管理器是分層的,因為Unity的容器時可以嵌套的,所以這個生命周期管理器就是針對這種情況,當使用了這種生命周期管理器,父容器 和子容器所維護的對象的生命周期是由各自的容器來管理,代碼如下(RegisterInstance情況也類似,這邊就不展示了)

public static void HierarchicalLifetimeManagerCode()
{
    container.RegisterType<IClass, MyClass>(new HierarchicalLifetimeManager());
    //創建子容器
    var childContainer = container.CreateChildContainer();
    childContainer.RegisterType<IClass, MyClass>(new HierarchicalLifetimeManager());
 
    Console.WriteLine("-------ContainerControlledLifetimeManager Begin------");
    Console.WriteLine("第一次調用父容器注冊的對象HashCode:" +
        container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第二次調用父容器注冊的對象HashCode:" +
        container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第一次調用子容器注冊的對象HashCode:" +
        childContainer.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第二次調用子容器注冊的對象HashCode:" +
        childContainer.Resolve<IClass>().GetHashCode());
    Console.WriteLine("-------ContainerControlledLifetimeManager End------");
}

由於配置文件不能配置這種層級效果,所以配置這種生命周期時只需要更改下生命周期名稱:

<register type="IClass" mapTo="MyClass" name="hl">
  <lifetime type="hierarchical" />
</register>

具體的效果圖如下,可以看出父級和子級維護不同對象實例:

pic74

這邊需要提一下的就是,Unity這種分級容器的好處就在於我們可以對於有不同生命周期的對象放在不同的容器中,如果一個子容器被釋放,不會影響到其它子容器中的對象,但是如果根節點處父容器釋放后,所有的子容器都將被釋放。

2.4 PerResolveLifetimeManager

這個生命周期是為了解決循環引用而重復引用的生命周期,先看一下微軟官方給出的實例:

public interface IPresenter
{ }
 
public class MockPresenter : IPresenter
{
    public IView View { get; set; }
 
    public MockPresenter(IView view)
    {
        View = view;
    }
}
 
public interface IView
{
    IPresenter Presenter { get; set; }
}
 
public class View : IView
{
    [Dependency]
    public IPresenter Presenter { get; set; }
}

從這個例子中可以看出,有2個接口IPresenter和IView,還有2個類MockPresenter和View分別實現這2個接口,同時這2個類 中都包含了對另外一個類的對象屬性,這個就是一個循環引用,而對應的這個生命周期管理就是針對這種情況而新增的,其類似於 TransientLifetimeManager,但是其不同在於,如果應用了這種生命周期管理器,則在第一調用的時候會創建一個新的對象,而再次通過 循環引用訪問到的時候就會返回先前創建的對象實例(單件實例),代碼如下:

public static void PerResolveLifetimeManagerCode()
{
    var container = new UnityContainer()
    .RegisterType<IPresenter, MockPresenter>()
    .RegisterType<IView, View>(new PerResolveLifetimeManager());
 
    var view = container.Resolve<IView>();
    var tempPresenter = container.Resolve<IPresenter>();
    var realPresenter = (MockPresenter)view.Presenter;
 
    Console.WriteLine("-------PerResolveLifetimeManager Begin------");
    Console.WriteLine("使用了PerResolveLifetimeManager的對象 Begin");
    Console.WriteLine("通過Resolve方法獲取的View對象:" +
        view.GetHashCode());
    Console.WriteLine("View對象中的Presenter對象所包含的View對象:" +
        realPresenter.View.GetHashCode());
    Console.WriteLine("使用了PerResolveLifetimeManager的對象 End");
    Console.WriteLine("");
    Console.WriteLine("未使用PerResolveLifetimeManager的對象 Begin");
    Console.WriteLine("View對象中的Presenter對象:" +
        realPresenter.GetHashCode());
    Console.WriteLine("通過Resolve方法獲取的View對象:" +
        tempPresenter.GetHashCode());
    Console.WriteLine("未使用PerResolveLifetimeManager的對象 End");
    Console.WriteLine("-------PerResolveLifetimeManager Begin------");
}

從代碼中可以看出,在注冊對象的時候,僅對IView和View應用了PerResolveLifetimeManager,所以第二次訪問View對象會返回同一實例。

具體配置文件如下,有關構造函數注入和屬性注入的內容在下一篇文章中進行介紹

<alias alias="IPresenter" type="UnityStudyConsole.IPresenter, UnityStudyConsole" />
<alias alias="IView" type="UnityStudyConsole.IView, UnityStudyConsole" />
<alias alias="MockPresenter" type="UnityStudyConsole.MockPresenter, UnityStudyConsole" />
<alias alias="View" type="UnityStudyConsole.View, UnityStudyConsole" />
<container name="Second">
  <register type="IPresenter" mapTo="MockPresenter">
    <constructor>
      <param name ="view" type="IView">
      </param>
    </constructor>
  </register>
  <register type="IView" mapTo="View" >
    <lifetime type="perresolve"/>
    <property name="Presenter" dependencyType="IPresenter"></property>
  </register>
</container>

讀取配置文件代碼類似於前面其他配置文件讀取代碼,這里就不展示,具體請看示例代碼。

具體的效果圖如下:

pic75

可以看出2次調用View對象的HashCode都是一樣的,而Presenter對象的HashCode不同。

2.5 PerThreadLifetimeManager

每線程生命周期管理器,就是保證每個線程返回同一實例,具體代碼如下:

public static void PerThreadLifetimeManagerCode()
{
    container.RegisterType<IClass, MyClass>(new PerThreadLifetimeManager());
    var thread = new Thread(new ParameterizedThreadStart(Thread1));
    Console.WriteLine("-------PerResolveLifetimeManager Begin------");
    Console.WriteLine("默認線程 Begin");
    Console.WriteLine("第一調用:" +
        container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第二調用:" +
        container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("默認線程 End");
    thread.Start(container);
} 
public static void Thread1(object obj)
{
    var tmpContainer = obj as UnityContainer;
    Console.WriteLine("新建線程 Begin");
    Console.WriteLine("第一調用:" +
        tmpContainer.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第二調用:" +
        tmpContainer.Resolve<IClass>().GetHashCode());
    Console.WriteLine("新建線程 End");

Console.WriteLine("-------PerResolveLifetimeManager End------"); 
}

有關配置相關的代碼與前面的生命周期管理器差不多,這邊就不貼代碼了,請看示例代碼。

具體效果圖如下:

pic76

同時需要注意的是,一般來說不建議在使用RegisterInstance對已存在的對象注冊關系時使用PerThreadLifetimeManager,因為此時的對象已經在一個線程內創建了,如果再使用這個生命周期管理器,將無法保證其正確調用。

 

2.6 ExternallyControlledLifetimeManager

外部控制生命周期管理器,這個 生命周期管理允許你使用RegisterType和RegisterInstance來注冊對象之間的關系,但是其只會對對象保留一個弱引用,其生命周期 交由外部控制,也就是意味着你可以將這個對象緩存或者銷毀而不用在意UnityContainer,而當其他地方沒有強引用這個對象時,其會被GC給銷毀 掉。

在默認情況下,使用這個生命周期管理器,每次調用Resolve都會返回同一對象(單件實例),如果被GC回收后再次調用Resolve方法將會重新創建新的對象,示例代碼如下:

public static void ExternallyControlledLifetimeManagerCode()
{
    container.RegisterType<IClass, MyClass>(new ExternallyControlledLifetimeManager());
    var myClass1 = container.Resolve<IClass>();
    var myClass2 = container.Resolve<IClass>();
    Console.WriteLine("-------ExternallyControlledLifetimeManager Begin------");
    Console.WriteLine("第一次調用:" +
        myClass1.GetHashCode());
    Console.WriteLine("第二次調用:" +
        myClass2.GetHashCode());
    myClass1 = myClass2 = null;
    GC.Collect();
    Console.WriteLine("****GC回收過后****");
    Console.WriteLine("第一次調用:" +
       container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第二次調用:" +
       container.Resolve<IClass>().GetHashCode());
 
    Console.WriteLine("-------ExternallyControlledLifetimeManager End------");
}

有關配置相關的代碼與前面的生命周期管理器差不多,這邊就不貼代碼了,請看示例代碼。

效果圖如下:

pic77

 

3.小結

由於自己在學習Unity,在查找相關資料,在園子里找到這篇,看了一下還是很不錯的,於是轉載了,原文鏈接:http://www.cnblogs.com/kyo-yo/archive/2010/11/10/Learning-EntLib-Tenth-Decoupling-Your-System-Using-The-Unity-PART2-Learn-To-Use-Unity-Two.html,里面的文章寫的蠻好的,大家可以查考。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM