2011-12-02 58 views
1

針對這裏我的問題,這是關於有一個循環依賴一半:構造函數注入循環通過反射解決了依賴關係?

C# ASP.NET Dependency Injection with IoC Container Complications

主講依賴於IVIEW。頁面(實現IView)取決於主持人。其他已經解決了從構造注入到財產注入的這種轉換

我沒有這樣做,因爲我覺得您現在需要製作可以在外部進行修改的公共屬性,並且也成爲開發人員初始化的責任。

但似乎我已經能夠解決這個問題的另一種方式。

通過具有默認構造函數以及重載參數化構造函數的頁面,您可以通過反射調用無參數構造函數來創建對象 然後通過注入依賴項來調用重載的對象。

I'ill通過示例示出了:

class TestObject 
{ 
    public string Name { get; set; } 

    public TestObject() 
    { 
     Name = "Constructed with no args"; 
     Console.WriteLine("constructor hash code: " + GetHashCode()); 
    } 

    public TestObject(string name) 
    { 
     this.Name = name; 
     Console.WriteLine("constructor hash code: " + GetHashCode()); 
    } 
} 

該目的可以簡單地構成:

var obj = Activator.CreateInstance<TestObject>(); 

var obj = new TestObject(); 

但是,我可以通過反射使用重載的構造注入依賴關係:

ConstructorInfo ctor = obj.GetType().GetConstructors()[1]; 
ctor.Invoke(obj, new[] { "injected" }); 

我能夠用這種方式來線了structuremap註冊創建後的實例,然後注入的依賴關係。

當然,我們可以使用常規方法來注入依賴關係,但是這又打破了封裝,所以您可以再次調用此 來覆蓋依賴關係。

而且作爲構造這個斜面簡單地通過靜態代碼到達。

但我不知道我是怎麼想的,感覺就像一個黑客或東西,我可以簡單地做偶然在C#中一點點。

我想聽聽您的想法

感謝。

+0

有趣的是,這是可能的,但我認爲這是需要長期維護的頭痛問題。想象一下,那些正在調試你的代碼的可憐的維護工程師,發現構造函數被調用了兩次,並且假定兩個實例必須以某種方式創建。 –

+0

相關:http://stackoverflow.com/questions/2053044/can-dependency-injection-prevent-a-circular-dependency –

回答

1

我不喜歡這種反射的使用,因爲它意味着您無法輕鬆創建一個IoC容器的視圖/演示者。求解IoC容器的需要

的一種方式是使用一個工廠能夠容易地創建一個視圖/主持人和放置反射邏輯在工廠。

然而,在這一點上,你可以有一個簡單的財產或Initialize(view)/Initialize(presenter)方法。調用這些方法的責任由工廠或IoC容器(其充當工廠)從開發人員手中帶走。

構建時將依賴項傳遞給對象的替代方法是將可以構建依賴項的工廠傳遞給對象。最簡單的工廠形式是一個簡單的Func

void Main() 
{ 
    var controller = Controller.Create (c => new View (c)); 
} 

class Controller { 
    private View view; 

    // This could also be a constructor, but I prefer to think of this 
    // as a factory method. 
    public static Controller Create (Func<Controller, View> viewBuilder) { 
     var controller = new Controller(); 
     var view = viewBuilder (controller); 
     controller.Initialize (view); 
     return controller; 
    } 

    protected Controller() { 
    } 

    protected void Initialize (View view) { 
     this.view = view; 
    } 
} 

class View { 
    private Controller controller; 

    public View (Controller controller) { 
     this.controller = controller; 
    } 
} 

如果你堅持要具有一對構造函數的我會做無參數的構造函數保護,除非它確實是有效的沒有一個主持人/視圖創建視圖/主持人。這會阻止某人錯誤地使用該構造函數,因爲在構建之後沒有反射來初始化視圖/演示者。

雖然我後來認爲這是一個稍微好一點的反射思想是將一個無參數的構造函數保護,以及一個受保護的初始化方法。

public class TestObject 
{ 
    protected TestObject() { 
    } 

    public TestObject (string name) { 
     Initialize (name) 
    } 

    protected Initialize (string name) {} 
} 

這強制您不能構造它沒有名稱,除非您使用反射。反射將被封裝在工廠中,以確保Initialize也被稱爲。

+0

雖然我很同意你說的有一個或兩個問題。首先,這裏的目標是DI ASP.NET。你不能使無參數的構造函數受到保護,頁面是由ASP創建的,並且你無法控制這一點,只能在處理程序工廠中將依賴關係注入到已創建的對象中。工廠是一個很好的解決方案,實際上它是解決方案之一,但我在我的文章中指出,這需要開發人員爲他的每個演示者創建一個工廠,或許您可以通過泛型完成此工作,但是im仍然試圖解決這個問題 – Andre

+0

Chris Chilvers的想法很有用,特別是當'Initialize'方法被公開時。當你的'System.Web.Page'類可以按照約定有一個公共'Initialize'方法時,你可以使用反射來查找這個方法,並將它需要的依賴注入到它中。雖然這仍然需要反思,但這將部分信任。在已經初始化的對象上調用一個重載的構造函數將不會在部分信任中起作用。仍然錯誤地輸入'Inatilize',沒有任何東西被注入。 – Steven

+0

克里斯抱歉,如果看起來我懷疑你的回答,史蒂文感謝讓我意識到這一點,是的,這是行得通的,是什麼讓我意識到,當你把初始化作爲一個慣例,從而使工廠知道該怎麼調用 – Andre