原文連結:http://www.doriandeng.cn/archives/97.html
Unity 初始化主要是註冊類型映射並指定其生命週期。
在本文中,我們使用了一個介面 IDialer、一個實現了介面的抽象基類 Dialer,二個繼承自 Dialer 的具體類 ButtonTypeDialer 和 FigurePlateDialer 類,以及一個使用 Dialer 的 Telephone 類。
生命週期管理
之所以將生命週期的管理放在開始,是因為Unity 會根據在類型的註冊時需要指定的生命週期來管理對象的建立和解析。
Unity 使用繼承自 LifetimeManager 基類的類來決定如何儲存對象執行個體的引用,以及如何銷毀對象。Unity 內建了二個用於生命週期管理的類:ContainerControlledLifetimeManager 和 ExternallyControlledLifetimeManager ,下面分別對這二個類進行描述。如果指定了這二種管理方式,無論是以 RegisterType 還是以 RegisterInstance 註冊的類型,都將處理成單件對象。
ContainerControlledLifetimeManager 類是指定 Unity 容器管理註冊的對象,即對象的生命週期與 Unity 容器一致,在銷毀 Unity 時將銷毀所有其管理的對象。同時,
ExternallyControlledLifetimeManager 類指定 Unity 僅保持對象的弱引用,對象的儲存和銷毀由 Unity 容器外部來控制。
如果不需要處理成單件對象,可以在調用 RegisterType 方法時不指定其 LifetimeManager 類,這樣 Unity 容器將使用臨時生命週期管理,即為每次對擷取對象的請求都建立一個新的執行個體。
用 RegisterType 註冊類型映射
RegisterType 包含多個支援泛型的重載,同時還包含了一一對應的非泛型重載。在此僅對其泛型重載進行闡述,非泛型重載與其對應。
RegisterType 註冊類型映射可以是註冊介面或基類所對應的類型,也可以直接註冊類型。對於前者,在此僅給出介面或基類註冊的一種,另一種直接替換即可。
RegisterType<TFrom, TTo>( )
此方法註冊一個預設的類型映射,並且為擷取對象的每次請求建立一個新的執行個體。例如如下代碼:
在上例中的代碼中,前二行即是對類型的註冊。運行此代碼,結果如下:
而這種註冊的對應的設定檔如下:
同時,將上面的代碼修改為如下代碼:
運行此代碼,我們將得到同樣的結果。
RegisterType<TFrom, TTo>(LifetimeManager lifetime)
此方法與上一方法的不同之處在於可以指定生命週期的管理。例如,我們有如下代碼:
我們將可以得到如下結果:
可以看出,在使用 ContainerControlledLifetimeManager 之後,二次擷取的都是同一對象,即 Unity 容器在第一次擷取時建立了一個對象後就將其儲存了下來供後繼擷取使用。
然後,我們將這幾行語句拆成二個方法,如下所示:
這時,我們可以得到與前面同樣的結果,讀者可以將其中的 ContainerControlledLifetimeManager 類替換成 ExternallyControlledLifetimeManager 類檢查這二個類所產生的不同效果。
如果要使用設定檔來完成註冊,我們可以使用如下配置:
同時,我們需要將上面的第 64 行代碼修改第22、23行代碼以應用設定檔。
RegisterType<TFrom, TTo>(String name)
此方法用於註冊一個命名的類型映射,而不是預設的類型註冊。如果 name 為 null,其作用將與 RegisterType<TFrom, TTo>( ) 方法相同。範例程式碼如下:
從上面的代碼可以看出,此方法的不同點在於在擷取對象的時候需要指定其類型註冊的名稱。相應的設定檔如下:
RegisterType<TFrom, TTo>(String name, LifetimeManager lifetime)
此方法是前面三種方法的組合,在此不再詳述。
其他
其他方法還包括 RegisterType<T>(LifetimeManager lifetime)、RegisterType<T>(String name, LifetimeManager lifetime)等方法,使用方法與前面類似,不再詳述。
用 RegisterInstance 註冊已存在的對象執行個體
RegisterInstance 用於將一個已建立的對象執行個體註冊成單件類型映射,預設情況下,使用 ContainerControlledLifetimeManager 生命週期管理。
RegisterInstance<T>(T instance)
此方法註冊一個預設的單件類型映射。範例程式碼如下:
在此樣本中,我們先建立了一個 ButtonTypeDialer 類型的對象,然後將其註冊為 Dialer 類型的映射,然後通過 Unity 來擷取它。
與 RegisterType 不同的是,它的設定檔如下所示:
在設定檔中,我們使用了一個 DialerConverter 類,這是一個類型轉換類。在 Instances 元素中 value 元素是必須的,如果要建立一個自訂類型的執行個體,就必須有一個合適的類型轉換類來將欄位串表示的 value 值轉換成需要的類型。相應的,我們的代碼修改如下:
注意:在Resolve 方法中指定的類型必須與設定檔中的類型一致,否則會觸發異常。
RegisterInstance<TInterface>(TInterface instance, LifetimeManager lifetime)
此方法在註冊一個已有對象的執行個體的類型映射的同時指定一個生命週期管理。範例程式碼如下:
大家可以將 ContainerControlledLifetimeManager 替換成 ExternallyControllerLifetimeManager 試試,這時會出現異常。
這個方法對應的設定檔是,遺憾的是,設定檔不支援此方法的生命週期的配置。
其他
其他還有 RegisterInstance<TInterface>(String name, TInterface instance) 和 RegisterInstance<TInterface>(String name, TInterface instance, LifetimeManager lifetime) 等方法,大家可以參考 RegisterType 方法進行學習。
Unity 容器的層次
Unity 容器支援容器的嵌套,那麼在什麼情況下需要使用容器的嵌套呢。如針對同一類型註冊多個類型映射,類型映射需要有不同的生命週期管理時,都需要使用嵌套。下面僅對同一類型註冊多個類型映射的情況進行解釋。
範例程式碼如下:
運行代碼,我們可以得到如下的結果:
可能有人會想,這樣還不如直接建立二個容器,但之所以這麼做,是為了所有對象都可以在父容器中進行管理,而且,子容器多用於管理一些臨時性的映射。另外,可以在子容器使用在你父容器中註冊的映射,也就是說,容器會自己向上搜尋註冊。
Unity 的設定檔並不直接支援容器的嵌套,這時,我們可以通過在設定檔中將父容器定義成預設的容器,而將子容器配置成命名的容器,然後將命名配置應用到子容器上即可。例如,我們有如下配置:
通過將代碼修改如下,我們將可以得到同樣的結果:
小結
通過上面的學習,我們已經全面瞭解了 Unity 依賴注入容器的初始化,在經過這些不同的註冊後,我們將可以通過不同的方式擷取對象,使用對象。
原始碼下載
look-into-unity-1-1-initunity.zip
希望對您有所協助!
鄧明
理想&美人