My.Ioc 代码示例——避免循环依赖
本文的目的在于通過一些示例,向大家說明 My.Ioc 支持哪些類型的依賴關系。也就是說,如何設計對象不會導致循環依賴。
在 Ioc 世界中,循環依賴是一個頑敵。這不僅因為它會導致 Ioc 容器拋出異常,而且還因為它是不可預知的,盡管通過仔細的配置是可以盡量避免這個問題的。
當我們在 Ioc 容器中注冊對象時,我們事先并不知道該對象與其他對象之間的依賴關系,因為依賴關系是由 Ioc 容器管理的。這種依賴關系要等到我們首次調用 container.Resolve(contractType) 時才能確定。而且,它還有可能會隨著所依賴對象的注冊/注銷而發生變化。
因此,要想避免循環依賴問題,那么在設計對象時,仔細地思考該對象與其他對象之間的依賴關系,審慎地區分不同對象的職責就顯得比較重要了。當然,對象設計是 Ioc 容器之外的事情了,我們沒有辦法加以約束。但是,即使您不使用 Ioc 容器,對象設計也是一個值得您深思熟慮的大問題。而一旦您選擇使用 Ioc,那也意味著您的對象設計需要更多圍繞 Ioc 來考慮。
其實,在 My.Ioc 中要想避免循環依賴也很簡單,只要遵循一個原則即可,那就是 [不要在對象構造過程中引入循環依賴]。下面我們結合示例代碼來講解。
using System; using System.Diagnostics; using My.Ioc; using My.Ioc.Exceptions;namespace HowToAvoidCyclicDependency {#region Test clazz#region Case 3public class Grade3{private readonly Room3 _room;public Grade3(Room3 room){_room = room;}public Room3 Room{get { return _room; }}}public class Room3{public Grade3 Grade { get; set; }}#endregion#region Case 2public class Grade2{private readonly Room2 _room;public Grade2(Room2 room){_room = room;room.Grade = this;}public Room2 Room{get { return _room; }}}public class Room2{public Grade2 Grade { get; set; }}#endregion#region Case 1public class Grade1{public Room1 Room { get; set; }}public class Room1{public Grade1 Grade { get; set; }}#endregion#endregionclass Program{static void Main(string[] args){var container = new ObjectContainer(false);Register(container);Resolve(container);Console.WriteLine("HowToAvoidCyclicDependency success...");Console.ReadLine();}static void Register(IObjectContainer container){container.Register<Grade3>().In(Lifetime.Transient());container.Register<Room3>().WithPropertyAutowired("Grade").In(Lifetime.Transient());container.Register<Grade2>().In(Lifetime.Transient());container.Register<Room2>().In(Lifetime.Transient());container.Register<Grade1>().WithPropertyAutowired("Room").In(Lifetime.Transient());container.Register<Room1>().WithPropertyAutowired("Grade").In(Lifetime.Transient());container.CommitRegistrations();}static void Resolve(IObjectContainer container){var grade1 = container.Resolve<Grade1>();Debug.Assert(grade1 == grade1.Room.Grade);var room1 = container.Resolve<Room2>();Debug.Assert(room1 == room1.Grade.Room);// No cyclic dependency in constructorvar grade2 = container.Resolve<Grade2>();Debug.Assert(grade2 == grade2.Room.Grade);try{// Cyclic dependency in constructor// The call path is: Grade => Room => Gradevar grade3 = container.Resolve<Grade3>();}catch (Exception ex){Debug.Assert(ex is CyclicDependencyException);}}} } View Code在示例代碼中,我們設計了三對 Grade/Room 類,用來演示三種不同的情況。
先來看 Grade1/Room1。
public class Grade1 {public Room1 Room { get; set; } } public class Room1 {public Grade1 Grade { get; set; } }我們看到這兩個類的屬性相互引用對方,但它們都沒有定義構造函數。這里,我們以 Grade1 為例來分析一下它們的構造過程。
當我們需要一個 Grade1 對象時,容器首先調用其構造函數為我們創建一個 Grade1 對象。創建好 Grade1 對象之后,我們看到它需要注入一個 Room1 類型的屬性。這樣容器又會為我們創建一個 Room1 對象。隨后,我們看到 Room1 也需要注入一個 Grade1 類型的屬性。此時,由于之前已經創建好一個 Grade1 對象,因此容器在這里便會復用該 Grade1 對象(這一點很關鍵),以完成 Room1 對象的創建。隨著 Room1 對象創建完成,容器跟著將該對象注入到 Grade1 的 Room 屬性中,這樣 Grade1 也創建完成了。因此,這里不會導致循環依賴,所以我們運行下面的代碼不會導致異常:
var grade1 = container.Resolve<Grade1>(); Debug.Assert(grade1 == grade1.Room.Grade); var room1 = container.Resolve<Room2>(); Debug.Assert(room1 == room1.Grade.Room);接下來,我們看 Grade2/Room2。
public class Grade2 {private readonly Room2 _room;public Grade2(Room2 room){_room = room;room.Grade = this;}public Room2 Room{get { return _room; }} } public class Room2 {public Grade2 Grade { get; set; } }當我們構造 Grade2 對象時,并不會引起循環依賴。因為我們看到 Grade2 雖然在構造函數中需要 Room2,但在構造 Room2 的過程中并不需要 Grade2(因為 Room2 沒有定義構造函數,而且也不需要在自身的構造過程中注入 Grade2 這個屬性),所以不會有循環依賴問題。
最后我們來看 Grade3/Room3。
public class Grade3 {private readonly Room3 _room;public Grade3(Room3 room){_room = room;}public Room3 Room{get { return _room; }} } public class Room3 {public Grade3 Grade { get; set; } }我們看到 Grade3 在構造函數中需要一個 Room3 類型參數,而 Room3 類型雖然沒有定義構造函數,但它在構造過程中要求注入一個 Grade3 類型的屬性值。這樣就引起了問題。因為當 Grade3 需要一個 Room3 對象的時候,Room3 對象尚未創建,這樣容器就需要先創建一個 Room3 對象。但容器在創建 Room3 對象的過程中,又需要注入一個 Grade3 對象。然而此時最初的 Grade3 尚未創建完成(其構造函數已經被調用,但尚未完成),還無法在此處復用。因此,容器又要再創建一個新的 Grade3 對象以滿足 Room3 的構造需要。隨后,這個新的 Grade3 又需要一個新的 Room3,而后面這個新的 Room3 又需要一個新的 Grade3...這樣永無休止地糾纏下去,這個構造過程永遠不會完成。如果用比較簡潔的方式來表述的話,上述創建過程的依賴路徑如下:Grade3 => Room3 => Grade3 => Room3 => Grade3....
這樣我們就明白了,只要我們在設計類時,注意避免產生如上所述的依賴路徑,就能夠有效地防止循環依賴的問題。其實,也就是遵循上面那個原則:[不要在對象構造過程中引入循環依賴]。
?
本文示例代碼以及 My.Ioc 框架源碼可在此處獲取。
轉載于:https://www.cnblogs.com/johnny-liu/p/3962814.html
總結
以上是生活随笔為你收集整理的My.Ioc 代码示例——避免循环依赖的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: visio 小技巧
- 下一篇: android多点触控自由对图片缩放